sfc-utils 1.3.63 → 1.3.65

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/brands2.js CHANGED
@@ -39,7 +39,9 @@ let getBrands2 = function(market){
39
39
  "twitter": "sfchronicle",
40
40
  "gaAccount": "UA-1616916-26",
41
41
  "subscribeLink": "https://www.sfchronicle.com/subproject",
42
- "sailCustomer": "fca2a0390286f0e53120a668534d9529"
42
+ "sailCustomer": "fca2a0390286f0e53120a668534d9529",
43
+ "sailSiteName": "san-francisco-chronicle",
44
+ "siteId": 35,
43
45
  }
44
46
  },
45
47
 
@@ -61,7 +63,9 @@ let getBrands2 = function(market){
61
63
  "invert": true,
62
64
  "gaAccount": "UA-1616916-24",
63
65
  "subscribeLink": "https://www.houstonchronicle.com/subproject",
64
- "sailCustomer": "48e30b5083cf6bf47c519651453c9e8a"
66
+ "sailCustomer": "48e30b5083cf6bf47c519651453c9e8a",
67
+ "sailSiteName": "houston-chronicle",
68
+ "siteId": 33
65
69
  }
66
70
  },
67
71
 
@@ -83,7 +87,9 @@ let getBrands2 = function(market){
83
87
  "invert": true,
84
88
  "gaAccount": "UA-1616916-7",
85
89
  "subscribeLink": "https://www.timesunion.com/subproject",
86
- "sailCustomer": "5bb9eee089bdc2e27cbd265535ad1f90"
90
+ "sailCustomer": "5bb9eee089bdc2e27cbd265535ad1f90",
91
+ "sailSiteName": "times-union",
92
+ "siteId": 3
87
93
  }
88
94
  },
89
95
 
@@ -100,7 +106,9 @@ let getBrands2 = function(market){
100
106
  "invert": true,
101
107
  "gaAccount": "UA-1616916-27",
102
108
  "subscribeLink": "https://www.expressnews.com/subproject",
103
- "sailCustomer": "aec52c4681ed63b5beab139a2584f0d4"
109
+ "sailCustomer": "aec52c4681ed63b5beab139a2584f0d4",
110
+ "sailSiteName": "san-antonio-express-news",
111
+ "siteId": 36
104
112
  }
105
113
  },
106
114
 
@@ -117,7 +125,9 @@ let getBrands2 = function(market){
117
125
  "invert": true,
118
126
  "gaAccount": "UA-1616916-99",
119
127
  "subscribeLink": "https://www.ctinsider.com/subproject",
120
- "sailCustomer": "9b21160afa226c9f84d27b47c3d52364"
128
+ "sailCustomer": "9b21160afa226c9f84d27b47c3d52364",
129
+ "sailSiteName": "ct-insider",
130
+ "siteId": 61
121
131
  }
122
132
  },
123
133
 
@@ -134,7 +144,9 @@ let getBrands2 = function(market){
134
144
  "invert": true,
135
145
  "gaAccount": "UA-1616916-99",
136
146
  "subscribeLink": "/subproject",
137
- "sailCustomer": "4a181de0b63a131cf27f8ea9485e5e1c"
147
+ "sailCustomer": "4a181de0b63a131cf27f8ea9485e5e1c",
148
+ "sailSiteName": "midland-reporter-telegram",
149
+ "siteId": 57
138
150
  }
139
151
  },
140
152
  Midcom: {
@@ -149,7 +161,9 @@ let getBrands2 = function(market){
149
161
  "invert": true,
150
162
  "gaAccount": "UA-1616916-99",
151
163
  "subscribeLink": "/subproject",
152
- "sailCustomer": "f3dc0abb53995f957783e76eff3ef01e"
164
+ "sailCustomer": "4a181de0b63a131cf27f8ea9485e5e1c",
165
+ "sailSiteName": "midland-daily-news",
166
+ "siteId": 61
153
167
  }
154
168
  },
155
169
  Conroe: {
@@ -164,7 +178,9 @@ let getBrands2 = function(market){
164
178
  "invert": true,
165
179
  "gaAccount": "UA-1616916-99",
166
180
  "subscribeLink": "https://www.yourconroenews.com/subproject",
167
- "sailCustomer": "4a181de0b63a131cf27f8ea9485e5e1c"
181
+ "sailCustomer": "4a181de0b63a131cf27f8ea9485e5e1c",
182
+ "sailSiteName": "the-courier-of-montgomery-county",
183
+ "siteId": 68
168
184
  }
169
185
  },
170
186
 
@@ -0,0 +1,53 @@
1
+ import React, { useEffect, useState, useRef } from "react"
2
+
3
+ const CaptionCreditSlideshow = ({ captionList, creditList, extraStyles }) => {
4
+ let captionCss = ["topper-image", "caption"];
5
+ if (extraStyles) captionCss = captionCss.concat(extraStyles);
6
+
7
+ const [index, setIndex] = useState(0);
8
+ const timeoutRef = useRef(null);
9
+
10
+ function resetTimeout() {
11
+ if (timeoutRef.current) {
12
+ clearTimeout(timeoutRef.current);
13
+ }
14
+ }
15
+
16
+ useEffect(() => {
17
+ resetTimeout();
18
+ timeoutRef.current = setTimeout(() => {
19
+ // Set current image index
20
+ setIndex((prevIndex) => (prevIndex === captionList.length - 1) ? 0 : prevIndex + 1);
21
+ },
22
+ 4000 // Delay for switching images
23
+ );
24
+
25
+ return () => {
26
+ resetTimeout();
27
+ };
28
+ }, [index]);
29
+
30
+ const hasCaption = (captionList.length > 0)
31
+ const hasCredit = (creditList.length > 0)
32
+ return (
33
+ <>
34
+ {hasCaption && hasCredit && (
35
+ <figcaption className={captionCss.join(' ')}>
36
+ {captionList[index]} <span className="credit">{creditList[index]}</span>
37
+ </figcaption>
38
+ )}
39
+ {!hasCaption && hasCredit && (
40
+ <figcaption className={captionCss.join(' ')}>
41
+ <span className="credit">{creditList[index]}</span>
42
+ </figcaption>
43
+ )}
44
+ {hasCaption && !hasCredit && (
45
+ <figcaption className={captionCss.join(' ')}>
46
+ {captionList[index]}
47
+ </figcaption>
48
+ )}
49
+ </>
50
+ )
51
+ }
52
+
53
+ export default CaptionCreditSlideshow
@@ -0,0 +1,95 @@
1
+ import React, { useEffect, useState, useRef } from "react"
2
+ import TopperImage from "../topperimage.mjs"
3
+ import * as styles from "../../styles/modules/imageslideshow.module.less"
4
+ import * as imageStyles from "../../styles/modules/topperimage.module.less"
5
+ import { TransitionGroup, CSSTransition } from "react-transition-group"
6
+
7
+ const ImageSlideshow = ({ wcmData, imageList, altList, topperStyle }) => {
8
+ const [index, setIndex] = useState(0);
9
+ const timeoutRef = useRef(null);
10
+
11
+ function resetTimeout() {
12
+ if (timeoutRef.current) {
13
+ clearTimeout(timeoutRef.current);
14
+ }
15
+ }
16
+
17
+ useEffect(() => {
18
+ resetTimeout();
19
+ timeoutRef.current = setTimeout(() => {
20
+ // Set current image index
21
+ setIndex((prevIndex) => (prevIndex === imageList.length - 1) ? 0 : prevIndex + 1);
22
+ },
23
+ 4000 // Delay for switching images
24
+ );
25
+
26
+ return () => {
27
+ resetTimeout();
28
+ };
29
+ }, [index]);
30
+
31
+ const getContainerClass = () => {
32
+ switch (topperStyle) {
33
+ case "stacked":
34
+ return styles.containerStacked;
35
+ case "full-screen":
36
+ return styles.containerStacked;
37
+ default:
38
+ return "";
39
+ }
40
+ }
41
+
42
+ const getWrapperClass = () => {
43
+ switch (topperStyle) {
44
+ case "stacked":
45
+ return styles.imageWrapperStacked;
46
+ case "full-screen":
47
+ return styles.imageWrapperFullscreen;
48
+ default:
49
+ return "";
50
+ }
51
+ }
52
+
53
+ const getImageClassList = () => {
54
+ switch (topperStyle) {
55
+ case "stacked":
56
+ return [imageStyles.cForceAspectRatio];
57
+ case "full-screen":
58
+ return [imageStyles.cImgSlideshowFullscreen];
59
+ default:
60
+ return [""];
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Overall CSS architecture:
66
+ * <Container>
67
+ * <Wrapper>
68
+ * <Image/>
69
+ * </Wrapper>
70
+ * </Container>
71
+ */
72
+ return (
73
+ <div className={getContainerClass()}>
74
+ <TransitionGroup>
75
+ <CSSTransition
76
+ key={index}
77
+ timeout={4000}
78
+ classNames={{
79
+ enter: styles.fadeEnter,
80
+ enterActive: styles.fadeEnterActive,
81
+ exit: styles.fadeExit,
82
+ exitActive: styles.fadeExitActive,
83
+ exitDone: styles.fadeExitDone
84
+ }}
85
+ >
86
+ <div className={getWrapperClass()}>
87
+ <TopperImage wcm={imageList[index]} alt={altList[index]} wcmData={wcmData} overrideCssList={getImageClassList()} />
88
+ </div>
89
+ </CSSTransition>
90
+ </TransitionGroup>
91
+ </div>
92
+ )
93
+ }
94
+
95
+ export default ImageSlideshow
@@ -1,38 +1,46 @@
1
1
  import React from "react"
2
2
  import Heading from "./heading.mjs"
3
- import WCMImage2 from "./wcmimage2.mjs"
3
+ import TopperImage from "./topperimage.mjs"
4
4
  import CaptionCredit from "./captioncredit.mjs"
5
+ import CaptionCreditSlideshow from "./slideshow/captioncreditslideshow.mjs"
6
+ import ImageSlideshow from "./slideshow/imageslideshow.mjs"
5
7
  import * as topperStyles from "../styles/modules/topper2.module.less"
8
+ import * as imageStyles from "../styles/modules/topperimage.module.less"
6
9
 
7
10
  const Topper2 = ({ settings, wcmData }) => {
8
- const {
9
- Topper_Style, Title, Title_Style, Deck, Image, Image_Alt, Image_Caption, Image_Credits,
10
- HeaderDek_Vertical_Position, HeaderDek_Vertical_Offset, HeaderDek_Horizontal_Offset, HeaderDek_Horizontal_Position, Inverted_Colors
11
+ const {
12
+ Topper_Style, Title, Title_Style, Deck, Image, Image_Alt, Image_Caption, Image_Credits,
13
+ HeaderDek_Vertical_Position, HeaderDek_Vertical_Offset, HeaderDek_Horizontal_Offset, HeaderDek_Horizontal_Position, Inverted_Colors
11
14
  } = settings
12
15
 
13
16
  const headerDekStyleList = () => {
14
- switch(Topper_Style) {
17
+ switch (Topper_Style) {
15
18
  case "stacked":
16
19
  case "no-visual":
17
20
  return [topperStyles.headerDekStacked, " mw-lg mt-lg mb-md"];
18
- case "full-screen":
19
- // apply margin offsets from spreadsheet
21
+ case "full-screen":
22
+ // Check if css ":root" is accessible
20
23
  if (typeof window != "undefined") {
24
+ // Apply margin offsets from spreadsheet
21
25
  calculatefullScreenOffsets();
22
26
  }
23
27
 
24
28
  return [
25
- topperStyles.headerDekFullScreen, fullScreenHorizontalCss(),
29
+ topperStyles.headerDekFullScreen,
30
+ // Add styling for header-deck container position
31
+ fullScreenHorizontalCss(),
32
+ // Add styling for text position inside header-deck
26
33
  ... (HeaderDek_Vertical_Position === "top") ? [topperStyles.headerDekTop] : [topperStyles.headerDekBottom],
34
+ // Add styling for header-deck container background
27
35
  ... (Inverted_Colors === "black-text-white-bg") ? [topperStyles.blackTextWhiteBg] : [topperStyles.whiteTextBlackBg]
28
36
  ];
29
- case "side-by-side":
37
+ case "side-by-side":
30
38
  return [];
31
39
  }
32
40
  }
33
41
 
34
- /** get horizontal positioning css for full screen header-deck container **/
35
- const fullScreenHorizontalCss = () => {
42
+ /** get horizontal positioning css for full screen header-deck container **/
43
+ const fullScreenHorizontalCss = () => {
36
44
  switch (HeaderDek_Horizontal_Position) {
37
45
  case "left": return topperStyles.headerDekLeft;
38
46
  case "right": return topperStyles.headerDekRight;
@@ -41,22 +49,23 @@ const Topper2 = ({ settings, wcmData }) => {
41
49
  }
42
50
 
43
51
  const headerStyleList = () => {
44
- let defaultStyles;
45
- switch(Topper_Style) {
46
- case "stacked":
52
+ let defaultStyles;
53
+ switch (Topper_Style) {
54
+ case "stacked":
47
55
  defaultStyles = [];
48
56
  break;
49
57
  case "no-visual":
50
58
  defaultStyles = ["left"];
51
59
  break;
52
- case "full-screen":
60
+ case "full-screen":
53
61
  defaultStyles = [topperStyles.hedFullScreen, fullScreenTextAlignCss()];
54
62
  break;
55
- case "side-by-side":
63
+ case "side-by-side":
56
64
  defaultStyles = [];
57
65
  break;
58
66
  }
59
67
 
68
+ // Apply extra custom styling if it exists in the spreadsheet
60
69
  if (Title_Style !== "") {
61
70
  let extraStyles = Title_Style.split(", ");
62
71
  defaultStyles = defaultStyles.concat(extraStyles);
@@ -66,14 +75,14 @@ const Topper2 = ({ settings, wcmData }) => {
66
75
  }
67
76
 
68
77
  const deckStyleList = () => {
69
- switch(Topper_Style) {
78
+ switch (Topper_Style) {
70
79
  case "stacked":
71
80
  return ["deck"];
72
- case "no-visual":
81
+ case "no-visual":
73
82
  return ["deck left"];
74
- case "full-screen":
83
+ case "full-screen":
75
84
  return ["deck", topperStyles.deckFullScreen, fullScreenTextAlignCss()];
76
- case "side-by-side":
85
+ case "side-by-side":
77
86
  return ["deck"];
78
87
  }
79
88
  }
@@ -87,18 +96,60 @@ const Topper2 = ({ settings, wcmData }) => {
87
96
  }
88
97
  }
89
98
 
90
- const ImageHTML = () => <WCMImage2 wcm={Image} alt={Image_Alt} isNotLazyloaded={false} wcmData={wcmData} isFullScreenTopper={false}/>
91
- const FullScreenImageHTML = () => <WCMImage2 wcm={Image} alt={Image_Alt} isNotLazyloaded={false} ratio={calculateFullScreenImageRatio()} wcmData={wcmData} isFullScreenTopper={true}/>
92
-
99
+ /** Converts wcm string from spreadsheet into a list of WCM ids */
100
+ const getWcmIdList = (listStr) => {
101
+ return listStr.split(";").map((d) => parseInt(d));
102
+ }
103
+
104
+ /** Checks if WCM list represents an image slideshow */
105
+ const isSlideshow = (wcmIdList) => {
106
+ return (wcmIdList.length > 1);
107
+ }
108
+
109
+ /** Converts string from spreadsheet into a list and pads the list if the length is incorrect */
110
+ const convertStringToList = (str, size) => {
111
+ var list = str.split(";");
112
+ if (list.length === size) return list;
113
+
114
+ for (var i = (list.length - 1); i < (size - 1); i++) {
115
+ list.push("");
116
+ }
117
+
118
+ console.error(`The topper slideshow does not have the correct amount of metadata!`);
119
+ return list;
120
+ }
121
+
122
+ const ImageHTML = () => <TopperImage wcm={Image} alt={Image_Alt} wcmData={wcmData} />
123
+ const FullScreenImageHTML = () => <TopperImage wcm={Image} alt={Image_Alt} wcmData={wcmData} overrideCssList={[imageStyles.cImgFullscreen]} />
124
+ const ImageSlideshowHTML = () =>
125
+ <ImageSlideshow
126
+ wcmData={wcmData}
127
+ imageList={wcmIdList}
128
+ altList={convertStringToList(Image_Alt, wcmIdList.length)}
129
+ topperStyle={Topper_Style}
130
+ />
131
+
132
+ const wcmIdList = getWcmIdList(Image);
93
133
  const TopperHtml = () => {
94
134
  switch (Topper_Style) {
95
- case "full-screen":
135
+ case "full-screen":
136
+ let containerCss = isSlideshow(wcmIdList) ? topperStyles.topperContainerSlideshowFullScreen : topperStyles.topperContainerFullScreen;
96
137
  return (
97
138
  <>
98
- <div className={topperStyles.topperContainerFullScreen}>
99
- <figure className={`topper-image ${topperStyles.imageFullScreen}` } aria-labelledby="topperCaptionText">
100
- <FullScreenImageHTML/>
101
- <CaptionCredit caption={Image_Caption} credit={Image_Credits} extraStyles={topperStyles.hideWhenDesktop}/>
139
+ <div className={containerCss}>
140
+ <figure className={`topper-image ${topperStyles.imageFullScreen}`} aria-labelledby="topperCaptionText">
141
+ {isSlideshow(wcmIdList) && <ImageSlideshowHTML />}
142
+ {!isSlideshow(wcmIdList) && <FullScreenImageHTML />}
143
+
144
+ {/* This caption-credit only shows when the screen size is tablet or mobile */}
145
+ {isSlideshow(wcmIdList) &&
146
+ <CaptionCreditSlideshow
147
+ captionList={convertStringToList(Image_Caption, wcmIdList.length)}
148
+ creditList={convertStringToList(Image_Credits, wcmIdList.length)}
149
+ extraStyles={topperStyles.hideWhenDesktop}
150
+ />
151
+ }
152
+ {!isSlideshow(wcmIdList) && <CaptionCredit caption={Image_Caption} credit={Image_Credits} extraStyles={topperStyles.hideWhenDesktop} />}
102
153
  </figure>
103
154
  <div className={headerDekStyleList().join(' ')}>
104
155
  <Heading level={1} text={Title}
@@ -110,74 +161,92 @@ const Topper2 = ({ settings, wcmData }) => {
110
161
  className={deckStyleList().join(' ')}
111
162
  />
112
163
  </div>
113
- </div>
114
- <div className="topperCaptionText">
115
- <CaptionCredit caption={Image_Caption} credit={Image_Credits} extraStyles={[topperStyles.hideWhenMobile, topperStyles.smallPaddingLeft]}/>
116
- </div>
164
+ </div>
165
+ <div className="topperCaptionText">
166
+ {/* This caption-credit only shows when the screen size is desktop */}
167
+ {isSlideshow(wcmIdList) &&
168
+ <CaptionCreditSlideshow
169
+ captionList={convertStringToList(Image_Caption, wcmIdList.length)}
170
+ creditList={convertStringToList(Image_Credits, wcmIdList.length)}
171
+ extraStyles={[topperStyles.hideWhenTablet, topperStyles.smallPaddingLeft]}
172
+ />
173
+ }
174
+ {!isSlideshow(wcmIdList) && <CaptionCredit caption={Image_Caption} credit={Image_Credits} extraStyles={[topperStyles.hideWhenTablet, topperStyles.smallPaddingLeft]} />}
175
+ </div>
117
176
  </>
118
177
  );
119
178
 
120
179
  case "stacked":
121
180
  return (
122
181
  <>
123
- <div>
124
- <div className={headerDekStyleList().join('')}>
125
- <Heading
126
- level={1}
127
- text={Title}
128
- className={headerStyleList().join(' ')}
129
- />
130
- <Heading
131
- level={2}
132
- text={Deck}
133
- className={deckStyleList().join(' ')}
134
- />
182
+ <div>
183
+ <div className={headerDekStyleList().join('')}>
184
+ <Heading
185
+ level={1}
186
+ text={Title}
187
+ className={headerStyleList().join(' ')}
188
+ />
189
+ <Heading
190
+ level={2}
191
+ text={Deck}
192
+ className={deckStyleList().join(' ')}
193
+ />
194
+ </div>
195
+ <figure className={`mw-xl ml-auto mr-auto ${topperStyles.imageStacked}`}>
196
+ {isSlideshow(wcmIdList) && <ImageSlideshowHTML />}
197
+ {!isSlideshow(wcmIdList) && <ImageHTML />}
198
+ {isSlideshow(wcmIdList) &&
199
+ <CaptionCreditSlideshow
200
+ captionList={convertStringToList(Image_Caption, wcmIdList.length)}
201
+ creditList={convertStringToList(Image_Credits, wcmIdList.length)}
202
+ extraStyles={[topperStyles.smallPaddingLeftWhenTablet]}
203
+ />
204
+ }
205
+ {!isSlideshow(wcmIdList) && <CaptionCredit caption={Image_Caption} credit={Image_Credits} extraStyles={[topperStyles.smallPaddingLeftWhenTablet]}/>}
206
+ </figure>
135
207
  </div>
136
- <figure className={`mw-xl ml-auto mr-auto ${topperStyles.imageStacked}`}>
137
- <ImageHTML/>
138
- <CaptionCredit caption={Image_Caption} credit={Image_Credits} />
139
- </figure>
140
- </div>
141
208
  </>
142
209
  );
143
210
 
144
211
  case "no-visual":
145
212
  return (
146
213
  <>
147
- <div>
148
- <div className={headerDekStyleList().join('')}>
149
- <Heading
150
- level={1}
151
- text={Title}
152
- className={headerStyleList().join(' ')}
153
- />
154
- <Heading
155
- level={2}
156
- text={Deck}
157
- className={deckStyleList().join(' ')}
158
- />
214
+ <div>
215
+ <div className={headerDekStyleList().join('')}>
216
+ <Heading
217
+ level={1}
218
+ text={Title}
219
+ className={headerStyleList().join(' ')}
220
+ />
221
+ <Heading
222
+ level={2}
223
+ text={Deck}
224
+ className={deckStyleList().join(' ')}
225
+ />
226
+ </div>
159
227
  </div>
160
- </div>
161
228
  </>
162
229
  );
163
230
  }
164
- }
231
+ }
165
232
 
233
+ /** Calculate offsets for header-deck container based on the spreadsheet, for full-screen toppers only **/
166
234
  const calculatefullScreenOffsets = () => {
167
235
  var r = document.querySelector(':root');
168
236
 
169
237
  let verticalOffset = convertStringToNumber(HeaderDek_Vertical_Offset, (HeaderDek_Vertical_Position === "bottom"));
170
238
  let horizontalOffset = convertStringToNumber(HeaderDek_Horizontal_Offset, (HeaderDek_Horizontal_Position === "right"));
171
239
 
172
- r.style.setProperty('--headerDek-vertical-offset', verticalOffset + "px" );
173
- r.style.setProperty('--headerDek-horizontal-offset', horizontalOffset + "px");
240
+ r.style.setProperty('--headerDek-vertical-offset', verticalOffset + "px");
241
+ r.style.setProperty('--headerDek-horizontal-offset', horizontalOffset + "px");
174
242
  }
175
243
 
244
+ /** Convert offset values from the spreadsheet into Number type **/
176
245
  const convertStringToNumber = (maybeStr, isFlipped) => {
177
246
  var num = 0;
178
- if (typeof(maybeStr) === "string") {
247
+ if (typeof (maybeStr) === "string") {
179
248
  // remove all non-number characters and "-" from string
180
- num = Number(maybeStr.replace(/(?!^)-|[^0-9-]/g,''))
249
+ num = Number(maybeStr.replace(/(?!^)-|[^0-9-]/g, ''))
181
250
  } else {
182
251
  num = maybeStr
183
252
  }
@@ -188,19 +257,9 @@ const Topper2 = ({ settings, wcmData }) => {
188
257
  return num;
189
258
  }
190
259
 
191
- const calculateFullScreenImageRatio = () => {
192
- // ratio needs to account for height of nav bar which is 37px
193
- if (typeof window === "undefined") return "56.25%"
194
- const windowRatio = ((window.innerHeight-37) / window.innerWidth)*100;
195
- let fullScreenRatio = "56.25%"; // defaults to 16/9;
196
-
197
- if (windowRatio < 56.25) fullScreenRatio = (windowRatio + "%")
198
- return fullScreenRatio
199
- }
200
-
201
- return (
202
- <TopperHtml/>
203
- )
204
- }
260
+ return (
261
+ <TopperHtml />
262
+ )
263
+ }
205
264
 
206
265
  export default Topper2
@@ -1,23 +1,23 @@
1
- import React, {useRef, useEffect, useState} from 'react'
1
+ import React, { useRef, useEffect, useState } from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import * as wcmimageStyles from "../styles/modules/wcmimage2.module.less"
3
+ import * as wcmimageStyles from "../styles/modules/topperimage.module.less"
4
4
  import { debounce } from './helpers/utilfunctions.mjs'
5
5
 
6
6
  const currentEnv = process.env.GATSBY_DEPLOY_ENV
7
7
 
8
- const ImageHTML = ({fullPath, imageRez, alt, isFullScreenTopper}) => {
8
+ const ImageHTML = ({ fullPath, imageRez, alt, overrideCssList }) => {
9
9
  let imageCss = [wcmimageStyles.cImg];
10
- if (isFullScreenTopper) imageCss.push(wcmimageStyles.cImgFullscreen);
10
+ if (overrideCssList) imageCss = overrideCssList
11
11
 
12
12
  return (
13
- <img
14
- className={imageCss.join(' ')}
15
- src={`${fullPath}${imageRez}x0.jpg`}
16
- alt={alt}/>
13
+ <img
14
+ className={imageCss.join(' ')}
15
+ src={`${fullPath}${imageRez}x0.jpg`}
16
+ alt={alt} />
17
17
  )
18
18
  }
19
19
 
20
- const WCMImage = ({ wcm, alt, ratio, wcmData, isFullScreenTopper }) => {
20
+ const TopperImage = ({ wcm, alt, ratio, wcmData, overrideCssList }) => {
21
21
  // When the wrapping div is rendered, we're going to figure out the best size for this image to be
22
22
  let picRef = useRef(null)
23
23
  let [imageRez, setImageRez] = useState(0)
@@ -25,10 +25,10 @@ const WCMImage = ({ wcm, alt, ratio, wcmData, isFullScreenTopper }) => {
25
25
  let setImageWidth = () => {
26
26
  let pixelWidth = 900 // Default width if we need a fallback
27
27
  let pixelRatio = window.devicePixelRatio || 1
28
- if (picRef.current && picRef.current.offsetWidth){
28
+ if (picRef.current && picRef.current.offsetWidth) {
29
29
  pixelWidth = Math.round(picRef.current.offsetWidth * pixelRatio)
30
30
  }
31
- if (imageRez < pixelWidth){ // No need to resize if we're just scaling down
31
+ if (imageRez < pixelWidth) { // No need to resize if we're just scaling down
32
32
  setImageRez(pixelWidth)
33
33
  }
34
34
  }
@@ -50,31 +50,32 @@ const WCMImage = ({ wcm, alt, ratio, wcmData, isFullScreenTopper }) => {
50
50
 
51
51
  // If this is actually undefined, return null
52
52
  // No error, nothing is rendered
53
- if (!wcm){
53
+ if (!wcm) {
54
54
  return null
55
55
  }
56
56
 
57
+ // During server-side rendering, access to ":root" is unavailable. This is okay; we just need
58
+ // to make sure that the site does not crash during SSR
59
+ let r = (typeof window != "undefined") ? document.querySelector(':root') : null;
60
+
57
61
  // This calculation is not used for the topper impl, but keeping it here just in case
58
62
  // it is needed for the general WCMImage utils migration
59
- let r = null;
60
- if (typeof window != "undefined") {
61
- r = document.querySelector(':root');
62
- }
63
-
64
63
  let photoRatio = "56.25%"; // Default to 16/9
65
64
  let fullPath = `https://s.hdnux.com/photos/0/0/0/${wcm}/0/`;
66
- if (!ratio){
65
+ if (!ratio) {
67
66
  let matchedPhoto = wcmData.nodes.find((item) => {
68
- return (item.photo.wcmid).toString() === (wcm).toString()
67
+ return (item.photo.wcmid).toString() === (wcm).toString()
69
68
  })
70
- if (!matchedPhoto && currentEnv !== "development"){
69
+ if (!matchedPhoto && currentEnv !== "development") {
71
70
  throw `WCMImage error: No matching ID for ${wcm} present in the array at the top of gatsby-node.js! If it's already there, you might need to reboot dev.`
72
- }
73
- if (matchedPhoto){
71
+ }
72
+ if (matchedPhoto) {
74
73
  // Set ratio of the actual photo like a legit hacker
75
- photoRatio = (matchedPhoto.photo.ratio*100)+"%";
76
- if (r) {
77
- r.style.setProperty('--img-bottom-padding-ratio', photoRatio);
74
+ photoRatio = (matchedPhoto.photo.ratio * 100) + "%";
75
+
76
+ // If css :root is available, set the photo ratio
77
+ if (r) {
78
+ r.style.setProperty('--img-bottom-padding-ratio', photoRatio);
78
79
  }
79
80
 
80
81
  fullPath = matchedPhoto.photo.full_path;
@@ -85,27 +86,31 @@ const WCMImage = ({ wcm, alt, ratio, wcmData, isFullScreenTopper }) => {
85
86
  } else {
86
87
  // If an override is being passed in, use that
87
88
  photoRatio = ratio
89
+
90
+ // If css :root is available, set the photo ratio
88
91
  if (r) {
89
- r.style.setProperty('--img-bottom-padding-ratio', photoRatio);
92
+ r.style.setProperty('--img-bottom-padding-ratio', photoRatio);
90
93
  }
91
94
  }
92
95
 
93
96
  // Get serious about alt tags
94
- if (typeof alt !== "string"){
97
+ if (typeof alt !== "string") {
95
98
  throw `WCMImage error: Image for ${wcm} needs an alt tag! Please add a good, descriptive alt tag. Suggestion from Mozilla: When choosing alt strings for your images, imagine what you would say when reading the page to someone over the phone without mentioning that there's an image on the page.`
96
99
  }
97
100
 
98
101
  return (
99
102
  <div ref={picRef}>
100
- <ImageHTML fullPath={fullPath} imageRez={imageRez} alt={alt} isFullScreenTopper={isFullScreenTopper}/>
103
+ <ImageHTML fullPath={fullPath} imageRez={imageRez} alt={alt} overrideCssList={overrideCssList} />
101
104
  </div>
102
105
  )
103
106
  }
104
107
 
105
- WCMImage.propTypes = {
108
+ TopperImage.propTypes = {
106
109
  wcm: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
107
110
  alt: PropTypes.string.isRequired,
108
- ratio: PropTypes.string
111
+ ratio: PropTypes.string, // This prop is currently not being used, might be ok to delete?
112
+ wcmData: PropTypes.object,
113
+ overrideCssList: PropTypes.array
109
114
  }
110
115
 
111
- export default WCMImage
116
+ export default TopperImage
package/copy/c2p_sheet.js CHANGED
@@ -38,7 +38,6 @@ let googleAuth = (configData) => {
38
38
  };
39
39
 
40
40
  let createSheet = (auth, fallback, configData) => {
41
- console.log(configData, "config data from create sheet");
42
41
  return new Promise((resolveAll, rejectAll) => {
43
42
  let gmail;
44
43
  if (!fallback) {
@@ -62,16 +61,21 @@ let createSheet = (auth, fallback, configData) => {
62
61
  }
63
62
 
64
63
  const drive = google.drive({ version: "v3", auth });
65
- const body = { title: "New C2P sheet" };
66
64
  const templateId = "1DUvYnFdxtBv1AXcDI9X00s_opUSu4uHJmd5LNemCN9E";
65
+ const body = { title: "New C2P sheet", name: configData.PROJECT.SLUG };
66
+ if (configData.PROJECT.MARKET_KEY === "SFC"){
67
+ // If this is SFC, make sure it's created in the shared folder
68
+ body.parents = ['1_jnRs3xOYDxm27y7TZB9gkudzTRmcd7x'];
69
+ }
70
+ const createOptions = {
71
+ fileId: templateId, // Base template
72
+ resource: body,
73
+ supportsAllDrives: true,
74
+ //'copyCollaborators': true // This doesn't work unfortunately
75
+ };
67
76
  drive.files.copy(
68
- {
69
- fileId: templateId, // Base template
70
- resource: body,
71
- //'copyCollaborators': true // This doesn't work unfortunately
72
- },
77
+ createOptions,
73
78
  (err, resp) => {
74
- console.log("SHEET RESP", resp);
75
79
  // Make edits to the sheet to match the repo details
76
80
  let resources = {
77
81
  auth: auth,
@@ -127,20 +131,17 @@ let createSheet = (auth, fallback, configData) => {
127
131
  );
128
132
  resolveAll();
129
133
  } else {
130
- // Transfer ownership to the user
131
134
  const permission = {
132
135
  type: "user",
133
- role: "owner",
134
- emailAddress: gmail
136
+ role: "writer",
137
+ emailAddress: gmail,
135
138
  };
136
139
  drive.permissions.create(
137
140
  {
138
141
  resource: permission,
139
142
  fileId: resp.data.id, // Modify the created file
140
- transferOwnership: true
141
143
  },
142
144
  (permErr, permResp) => {
143
- //console.log("permResp", permResp, permErr)
144
145
  if (permErr) {
145
146
  console.log("An error prevented the sharing of this sheet!");
146
147
  rejectAll();
@@ -11,10 +11,22 @@ let topperData = require("./src/data/topper2_settings.sheet.json");
11
11
  // NOTE: Leave this as an empty array if you aren't importing any WCM photos, but you won't be able to use the WCMImage component
12
12
  const wcmPhotos = [20374215]
13
13
 
14
- // Add topper image to wcmPhotos list
14
+ const addValidWcmId = (numStr, wcmPhotos) => {
15
+ let isNum = /^\d+$/.test(numStr.trim())
16
+ if (isNum && !wcmPhotos.includes(numStr.trim())) wcmPhotos.push((numStr.trim()));
17
+ }
18
+
19
+ // Add topper image(s) to wcmPhotos list
15
20
  let numStr = (topperData[0].Image).toString()
16
- let isNum = /^\d+$/.test(numStr.trim())
17
- if (isNum && !wcmPhotos.includes(numStr.trim())) wcmPhotos.push((numStr.trim()));
21
+ if (numStr.includes(";")) {
22
+ let numList = numStr.split(";");
23
+ for (var num of numList) {
24
+ addValidWcmId(num, wcmPhotos);
25
+ }
26
+ } else {
27
+ addValidWcmId(numStr, wcmPhotos);
28
+ }
29
+
18
30
 
19
31
  // Create nodes so GraphQL can access
20
32
  exports.sourceNodes = async ({
@@ -3903,6 +3903,11 @@
3903
3903
  "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
3904
3904
  "dev": true
3905
3905
  },
3906
+ "chain-function": {
3907
+ "version": "1.0.1",
3908
+ "resolved": "https://registry.npmjs.org/chain-function/-/chain-function-1.0.1.tgz",
3909
+ "integrity": "sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg=="
3910
+ },
3906
3911
  "chalk": {
3907
3912
  "version": "4.1.2",
3908
3913
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -4995,6 +5000,14 @@
4995
5000
  "utila": "~0.4"
4996
5001
  }
4997
5002
  },
5003
+ "dom-helpers": {
5004
+ "version": "3.4.0",
5005
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
5006
+ "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
5007
+ "requires": {
5008
+ "@babel/runtime": "^7.1.2"
5009
+ }
5010
+ },
4998
5011
  "dom-serializer": {
4999
5012
  "version": "1.4.1",
5000
5013
  "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
@@ -11024,6 +11037,18 @@
11024
11037
  "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz",
11025
11038
  "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw=="
11026
11039
  },
11040
+ "react-transition-group": {
11041
+ "version": "1.2.1",
11042
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-1.2.1.tgz",
11043
+ "integrity": "sha512-CWaL3laCmgAFdxdKbhhps+c0HRGF4c+hdM4H23+FI1QBNUyx/AMeIJGWorehPNSaKnQNOAxL7PQmqMu78CDj3Q==",
11044
+ "requires": {
11045
+ "chain-function": "^1.0.0",
11046
+ "dom-helpers": "^3.2.0",
11047
+ "loose-envify": "^1.3.1",
11048
+ "prop-types": "^15.5.6",
11049
+ "warning": "^3.0.0"
11050
+ }
11051
+ },
11027
11052
  "read": {
11028
11053
  "version": "1.0.7",
11029
11054
  "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
@@ -12938,6 +12963,14 @@
12938
12963
  }
12939
12964
  }
12940
12965
  },
12966
+ "warning": {
12967
+ "version": "3.0.0",
12968
+ "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
12969
+ "integrity": "sha512-jMBt6pUrKn5I+OGgtQ4YZLdhIeJmObddh6CsibPxyQ5yPZm1XExSyzC1LCNX7BzhxWgiHmizBWJTHJIjMjTQYQ==",
12970
+ "requires": {
12971
+ "loose-envify": "^1.0.0"
12972
+ }
12973
+ },
12941
12974
  "watchpack": {
12942
12975
  "version": "2.4.0",
12943
12976
  "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sfc-utils",
3
- "version": "1.3.63",
3
+ "version": "1.3.65",
4
4
  "author": "ewagstaff <evanjwagstaff@gmail.com>",
5
5
  "dependencies": {
6
6
  "archieml": "^0.4.2",
@@ -10,6 +10,7 @@
10
10
  "htmlparser2": "4.1.0",
11
11
  "opn": "^6.0.0",
12
12
  "prop-types": "^15.8.1",
13
+ "react-transition-group": "^4.4.5",
13
14
  "write": "^2.0.0"
14
15
  }
15
16
  }
@@ -0,0 +1,62 @@
1
+ @import (less) '../values';
2
+
3
+ .container-stacked {
4
+ margin: 0 auto;
5
+ white-space: nowrap;
6
+ padding-bottom: calc(@xl*2/3);
7
+
8
+ @media @tablet {
9
+ padding-bottom: calc(100vw * (2/3));
10
+ }
11
+ }
12
+
13
+ .container-fullscreen {
14
+ padding-bottom: calc(100vh - 37px);
15
+ width: 100%;
16
+
17
+ @media @tablet {
18
+ margin: 0 auto;
19
+ white-space: nowrap;
20
+ padding-bottom: calc(100vw * (2/3));
21
+ }
22
+ }
23
+
24
+ .image-wrapper-stacked {
25
+ position: absolute;
26
+ width: 100%;
27
+ }
28
+
29
+ .image-wrapper-fullscreen {
30
+ position: absolute;
31
+ width: 100%;
32
+
33
+ @media @mobile {
34
+ margin: 0;
35
+ padding: 0;
36
+ height: auto;
37
+ }
38
+ }
39
+
40
+ /* enter animation */
41
+ .fade-enter {
42
+ opacity: 0;
43
+ }
44
+
45
+ .fade-enter.fade-enter-active {
46
+ opacity: 1;
47
+ transition: opacity 1500ms linear;
48
+ }
49
+
50
+ /* exit animation */
51
+ .fade-exit {
52
+ opacity: 1;
53
+ }
54
+
55
+ .fade-exit.fade-exit-active {
56
+ opacity: 0;
57
+ transition: opacity 2000ms linear;
58
+ }
59
+
60
+ .fade-exit-done {
61
+ opacity: 0;
62
+ }
@@ -1,49 +1,66 @@
1
1
  @import (less) "../variables.less";
2
2
 
3
- :root {
4
- --headerDek-vertical-offset: 0px;
5
- --headerDek-horizontal-offset: 0px;
3
+ :root {
4
+ --headerDek-vertical-offset: 0px;
5
+ --headerDek-horizontal-offset: 0px;
6
6
  }
7
7
 
8
8
  .topperContainerFullScreen {
9
- max-width: 100%;
10
- position: relative;
9
+ max-width: 100%;
10
+ position: relative;
11
+ }
12
+
13
+ .topperContainerSlideshowFullScreen {
14
+ max-width: 100%;
15
+ position: relative;
16
+ height: calc(100vh - 37px);
17
+
18
+ @media @tablet {
19
+ height: auto;
20
+ }
11
21
  }
12
22
 
13
23
  .imageFullScreen {
14
- top: 0 !important;
15
- width: 100%;
24
+ top: 0 !important;
25
+ width: 100%;
26
+
27
+ @media @mobile {
28
+ height: 100%;
29
+ }
16
30
  }
17
31
 
18
32
  .imageStacked {
19
- position: relative;
20
- overflow: hidden;
33
+ position: relative;
34
+ max-width: @xl;
21
35
 
22
- @media @mobile {
23
- width: 100%;
24
- }
36
+ @media @mobile {
37
+ width: 100%;
38
+ }
25
39
  }
26
40
 
27
41
  .headerDekStacked {
28
42
  margin-left: auto;
29
43
  margin-right: auto;
44
+
30
45
  @media (max-width: 960px) {
31
46
  margin-left: 0;
32
47
  margin-right: 0;
33
48
  }
34
49
  }
50
+
35
51
  .headerDekFullScreen {
36
- position: absolute;
37
- padding: @s24;
38
- width: @md;
39
- @media @tablet {
40
- width: @sm;
41
- position: static;
42
- transform: none;
43
- margin: 0;
44
- padding: 0;
45
- width: 100%;
46
- }
52
+ position: absolute;
53
+ padding: @s24;
54
+ width: @md;
55
+
56
+ @media @tablet {
57
+ width: @sm;
58
+ position: static;
59
+ transform: none;
60
+ margin: 0;
61
+ padding: 0;
62
+ width: 100%;
63
+ }
47
64
  }
48
65
 
49
66
  .hedFullScreen {
@@ -51,51 +68,55 @@
51
68
  }
52
69
 
53
70
  .deckFullScreen {
54
- margin-bottom: 0px;
71
+ margin-bottom: 0px;
55
72
  }
56
73
 
57
74
  .headerDekTop {
58
75
  top: 0%;
59
76
  margin-top: var(--headerDek-vertical-offset);
77
+
60
78
  @media @tablet {
61
- margin-top: 0px;
79
+ margin-top: 0px;
62
80
  }
63
81
  }
64
82
 
65
83
  .headerDekBottom {
66
84
  bottom: 0%;
67
85
  margin-bottom: var(--headerDek-vertical-offset);
86
+
68
87
  @media @tablet {
69
- margin-bottom: 0px;
88
+ margin-bottom: 0px;
70
89
  }
71
90
  }
72
91
 
73
92
  .headerDekLeft {
74
93
  left: 0%;
75
94
  margin-left: var(--headerDek-horizontal-offset);
95
+
76
96
  @media @tablet {
77
- margin-left: 0px;
97
+ margin-left: 0px;
78
98
  }
79
99
  }
80
100
 
81
101
  .headerDekRight {
82
102
  right: 0%;
83
103
  margin-right: var(--headerDek-horizontal-offset);
104
+
84
105
  @media @tablet {
85
- margin-right: 0px;
106
+ margin-right: 0px;
86
107
  }
87
108
  }
88
109
 
89
110
  .headerDekCenter {
90
- left: 50%;
111
+ left: 50%;
91
112
  transform: translate(-50%, 0%);
92
113
  margin-left: var(--headerDek-horizontal-offset);
93
114
 
94
- @media @tablet {
95
- transform: none;
96
- top: 0%;
97
- left: 0%;
98
- margin-left: 0px;
115
+ @media @tablet {
116
+ transform: none;
117
+ top: 0%;
118
+ left: 0%;
119
+ margin-left: 0px;
99
120
  }
100
121
  }
101
122
 
@@ -105,6 +126,7 @@
105
126
 
106
127
  .textAlignCenter {
107
128
  text-align: center;
129
+
108
130
  @media @tablet {
109
131
  text-align: left;
110
132
  }
@@ -113,6 +135,7 @@
113
135
  .whiteTextBlackBg {
114
136
  color: @white;
115
137
  background-color: rgba(0, 0, 0, 0.54);
138
+
116
139
  @media @tablet {
117
140
  color: @black;
118
141
  background-color: transparent;
@@ -124,15 +147,16 @@
124
147
  background-color: rgba(255, 255, 255, 0.54);
125
148
  }
126
149
 
127
- .hideWhenMobile {
128
- @media @mobile {
129
- display: none;
150
+ .hideWhenTablet {
151
+ @media @tablet {
152
+ display: none;
130
153
  }
131
154
  }
132
155
 
133
156
  .hideWhenDesktop {
134
157
  display: none;
135
- @media @mobile {
158
+
159
+ @media @tablet {
136
160
  display: block;
137
161
  padding-left: @s8;
138
162
  }
@@ -140,4 +164,10 @@
140
164
 
141
165
  .smallPaddingLeft {
142
166
  padding-left: @s8;
167
+ }
168
+
169
+ .smallPaddingLeftWhenTablet {
170
+ @media @tablet {
171
+ padding-left: @s8;
172
+ }
143
173
  }
@@ -1,10 +1,35 @@
1
1
  @import (less) '../values';
2
2
  @import (less) "../variables.less";
3
3
 
4
- :root {
4
+ :root {
5
5
  --img-bottom-padding-ratio: "56.25%";
6
6
  }
7
7
 
8
+ .c-force-aspect-ratio {
9
+ display: static;
10
+ width: @xl;
11
+ object-fit: cover;
12
+ height: calc(@xl*2/3);
13
+ margin: 0 auto;
14
+
15
+ @media @tablet {
16
+ height: calc(100vw*2/3);
17
+ }
18
+ }
19
+
20
+ .c-img-slideshow-fullscreen {
21
+ display: static;
22
+ width: 100%;
23
+ object-fit: cover;
24
+ height: calc(100vh - 37px);
25
+ margin: 0 auto;
26
+
27
+ @media @tablet {
28
+ width: @xl;
29
+ height: calc(100vw*2/3);
30
+ }
31
+ }
32
+
8
33
  .c-img {
9
34
  display: block;
10
35
  position: relative;
@@ -17,7 +42,9 @@
17
42
  .c-img-fullscreen {
18
43
  position: static;
19
44
  object-fit: cover;
45
+ width: 100%;
20
46
  height: calc(100vh - 37px);
47
+
21
48
  @media @tablet {
22
49
  height: auto;
23
50
  }
@@ -25,7 +52,8 @@
25
52
 
26
53
  .c-fig-hidden-when-desktop {
27
54
  display: none;
28
- @media @mobile {
55
+
56
+ @media @tablet {
29
57
  display: block;
30
58
  padding-left: @s8;
31
59
  }
@@ -94,6 +94,7 @@
94
94
  .pa-xl {padding: @s40;}
95
95
  .pa-xxl {padding: @s48;}
96
96
 
97
+ .mw-xs {max-width: @xs;}
97
98
  .mw-sm {max-width: @sm;}
98
99
  .mw-md {max-width: @md;}
99
100
  .mw-lg {max-width: @lg;}