sfc-utils 1.4.202 → 1.4.204
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/.playwright-mcp/console-2026-04-02T20-52-32-578Z.log +994 -0
- package/.playwright-mcp/page-2026-04-02T20-52-43-138Z.yml +13584 -0
- package/.playwright-mcp/page-2026-04-02T20-52-52-183Z.png +0 -0
- package/components/geocoder.mjs +1 -0
- package/components/helpers/utilfunctions.mjs +17 -24
- package/components/layout/layouthelmet.mjs +4 -0
- package/footer.js +54 -0
- package/index.js +4 -0
- package/package.json +1 -1
- package/settings.js +2 -2
- package/example/.prettierrc +0 -5
- package/example/README.md +0 -8
- package/example/gatsby-config.js +0 -137
- package/example/gatsby-node.js +0 -66
- package/example/package-lock.json +0 -31884
- package/example/package.json +0 -71
- package/example/project-config.json +0 -38
- package/example/src/components/layout.js +0 -96
- package/example/src/components/sfc/ad.js +0 -47
- package/example/src/components/sfc/ai2html/ai2html_template.ai +5 -1368
- package/example/src/components/sfc/button.js +0 -40
- package/example/src/components/sfc/byline.js +0 -42
- package/example/src/components/sfc/component-helpers/customhooks.js +0 -17
- package/example/src/components/sfc/component-helpers/datehelpers.js +0 -55
- package/example/src/components/sfc/component-helpers/newsletterhelpers.js +0 -89
- package/example/src/components/sfc/component-helpers/requesthelpers.js +0 -7
- package/example/src/components/sfc/component-helpers/scrolldownhelpers.js +0 -11
- package/example/src/components/sfc/component-helpers/utilfunctions.js +0 -68
- package/example/src/components/sfc/creditline.js +0 -35
- package/example/src/components/sfc/credits.js +0 -17
- package/example/src/components/sfc/creditssection.js +0 -49
- package/example/src/components/sfc/dropcap.js +0 -15
- package/example/src/components/sfc/footer.js +0 -36
- package/example/src/components/sfc/geocoder.js +0 -148
- package/example/src/components/sfc/misccredit.js +0 -17
- package/example/src/components/sfc/navtop.js +0 -121
- package/example/src/components/sfc/newsletter.js +0 -157
- package/example/src/components/sfc/relatedlink.js +0 -23
- package/example/src/components/sfc/relatedrow.js +0 -23
- package/example/src/components/sfc/relatedsection.js +0 -27
- package/example/src/components/sfc/safelink.js +0 -86
- package/example/src/components/sfc/scrolldown.js +0 -22
- package/example/src/components/sfc/sharebuttons.js +0 -93
- package/example/src/components/sfc/topper.js +0 -88
- package/example/src/components/sfc/video.js +0 -74
- package/example/src/components/sfc/wcmimage.js +0 -131
- package/example/src/data/sfc/images/react.gif +0 -0
- package/example/src/data/sfc/related_links.json +0 -17
- package/example/src/html.js +0 -41
- package/example/src/pages/index.js +0 -166
- package/example/src/styles/credittooltip.less +0 -55
- package/example/src/styles/footer.less +0 -378
- package/example/src/styles/modules/ad.module.less +0 -21
- package/example/src/styles/modules/ai2html.module.less +0 -19
- package/example/src/styles/modules/button.module.less +0 -94
- package/example/src/styles/modules/byline.module.less +0 -11
- package/example/src/styles/modules/creditline.module.less +0 -12
- package/example/src/styles/modules/credits.module.less +0 -7
- package/example/src/styles/modules/creditssection.module.less +0 -17
- package/example/src/styles/modules/dropcap.module.less +0 -18
- package/example/src/styles/modules/geocoder.module.less +0 -79
- package/example/src/styles/modules/newsletter.module.less +0 -147
- package/example/src/styles/modules/relatedlink.module.less +0 -28
- package/example/src/styles/modules/relatedrow.module.less +0 -8
- package/example/src/styles/modules/relatedsection.module.less +0 -19
- package/example/src/styles/modules/scrolldown.module.less +0 -31
- package/example/src/styles/modules/share.module.less +0 -22
- package/example/src/styles/modules/topper.module.less +0 -63
- package/example/src/styles/modules/video.module.less +0 -27
- package/example/src/styles/modules/wcmimage.module.less +0 -20
- package/example/src/styles/nav.less +0 -187
- package/example/src/styles/old css/defaults.less +0 -99
- package/example/src/styles/old css/footer.less +0 -345
- package/example/src/styles/old css/nav.less +0 -187
- package/example/src/styles/old css/project.less +0 -1
- package/example/src/styles/old css/reset.css +0 -95
- package/example/src/styles/old css/seed.less +0 -7
- package/example/src/styles/old css/typography.css +0 -168
- package/example/src/styles/old css/values.less +0 -74
- package/example/src/styles/project.less +0 -1
- package/example/src/styles/reset.css +0 -97
- package/example/src/styles/seed.less +0 -6
- package/example/src/styles/values.less +0 -203
- package/example/src/styles/variables.less +0 -142
- package/example/static/manifest.webmanifest +0 -1
- package/example/tasks/create-c2p-sheet.js +0 -6
- package/example/tasks/deploy-addon.py +0 -14
- package/example/tasks/google-docs.js +0 -16
- package/example/tasks/google-sheets.js +0 -17
- package/example/tasks/node-helpers.js +0 -81
- package/example/tasks/post-build.sh +0 -7
- package/example/tasks/pre-build.sh +0 -18
- package/example/tempsettings.js +0 -28
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import React, {useState, useEffect} from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import Headroom from 'react-headroom'
|
|
4
|
-
import { getNav, appCheck } from '../../../../index'
|
|
5
|
-
import * as navStyles from '../../styles/nav.less'
|
|
6
|
-
import { setRichieParam } from './component-helpers/utilfunctions'
|
|
7
|
-
let projectConfig;
|
|
8
|
-
let navUrl;
|
|
9
|
-
let navText;
|
|
10
|
-
try {
|
|
11
|
-
projectConfig = require("../../project-config.json")
|
|
12
|
-
navUrl = projectConfig[0].NavLink
|
|
13
|
-
navText = projectConfig[0].NavTitle
|
|
14
|
-
} catch (err){
|
|
15
|
-
// do nothing
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (projectConfig){
|
|
19
|
-
// Conditional logic/rendering goes here
|
|
20
|
-
}
|
|
21
|
-
const NavTop = ({ meta, url_add = '' }) => {
|
|
22
|
-
let [showSubnav, setShowSubnav] = useState(false)
|
|
23
|
-
let [navClass, setNavClass] = useState("")
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
getNav params:
|
|
27
|
-
1. meta
|
|
28
|
-
2. url_add
|
|
29
|
-
3. force color ("white", "black" or null)
|
|
30
|
-
4. navlink object {
|
|
31
|
-
text,
|
|
32
|
-
url,
|
|
33
|
-
target
|
|
34
|
-
}
|
|
35
|
-
5. array of link objects for subnav
|
|
36
|
-
6. if true, show subscribe button instead of share buttons
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
// Override these with link objects (example below) if you want to customize
|
|
40
|
-
let navLink
|
|
41
|
-
if(navUrl || navText){
|
|
42
|
-
navLink = {
|
|
43
|
-
url: navUrl,
|
|
44
|
-
text: navText,
|
|
45
|
-
target: "_self",
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
let sectionArray = null
|
|
49
|
-
|
|
50
|
-
// Enable the social popup
|
|
51
|
-
let handleNavClick = (e) => {
|
|
52
|
-
// Handle facebook
|
|
53
|
-
let el = e.target.closest("#topper-nav-facebook-icon");
|
|
54
|
-
if (el && e.currentTarget.contains(el)) {
|
|
55
|
-
const link = el.getAttribute("href");
|
|
56
|
-
e.preventDefault()
|
|
57
|
-
window.open(
|
|
58
|
-
link,
|
|
59
|
-
'facebook-share-dialog',
|
|
60
|
-
'width=626,height=436'
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
// Handle subnav (but only if there are nav items)
|
|
64
|
-
if (sectionArray && sectionArray.length > 0){
|
|
65
|
-
el = e.target.closest("#nav-title");
|
|
66
|
-
if (el && e.currentTarget.contains(el)) {
|
|
67
|
-
e.preventDefault()
|
|
68
|
-
setShowSubnav(!showSubnav)
|
|
69
|
-
}
|
|
70
|
-
// Handle closing subnav
|
|
71
|
-
el = e.target.closest("#subnav");
|
|
72
|
-
if (el && e.currentTarget.contains(el)) {
|
|
73
|
-
setShowSubnav(false)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
//If this is CT, we need to reset state
|
|
79
|
-
let startingHTML = "";
|
|
80
|
-
if (meta.PROJECT.MARKET_KEY !== "CT"){
|
|
81
|
-
startingHTML = getNav(meta, url_add, null, navLink, sectionArray)
|
|
82
|
-
}
|
|
83
|
-
let [navHTML, setNavHTML] = useState(startingHTML)
|
|
84
|
-
|
|
85
|
-
// Setting nav now so that other CT markets can generate
|
|
86
|
-
useEffect(() => {
|
|
87
|
-
sectionArray = setRichieParam(sectionArray)
|
|
88
|
-
setNavHTML(getNav(meta, url_add, null, navLink, sectionArray))
|
|
89
|
-
|
|
90
|
-
// Set the navClass
|
|
91
|
-
let tempNavClass = ""
|
|
92
|
-
if (showSubnav){
|
|
93
|
-
tempNavClass += " show-subnav"
|
|
94
|
-
}
|
|
95
|
-
if (!appCheck()){
|
|
96
|
-
tempNavClass += " not-app"
|
|
97
|
-
}
|
|
98
|
-
setNavClass(tempNavClass)
|
|
99
|
-
}, [])
|
|
100
|
-
|
|
101
|
-
return (
|
|
102
|
-
<Headroom
|
|
103
|
-
style={{
|
|
104
|
-
zIndex: '2147483647',
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
<div className={navClass}
|
|
108
|
-
onClick={(e) => {handleNavClick(e)}}
|
|
109
|
-
dangerouslySetInnerHTML={{__html: navHTML}}
|
|
110
|
-
/>
|
|
111
|
-
</Headroom>
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
NavTop.propTypes = {
|
|
116
|
-
meta: PropTypes.object.isRequired,
|
|
117
|
-
url_add: PropTypes.string,
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
export default NavTop
|
|
121
|
-
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react'
|
|
2
|
-
// Grab font awesome
|
|
3
|
-
import { useStaticQuery, graphql } from "gatsby"
|
|
4
|
-
import {
|
|
5
|
-
newsletterPromo,
|
|
6
|
-
handleSubmit,
|
|
7
|
-
} from './component-helpers/newsletterhelpers'
|
|
8
|
-
import * as newsletterStyles from '../../styles/modules/newsletter.module.less'
|
|
9
|
-
import { getBrands } from '../../../../index'
|
|
10
|
-
|
|
11
|
-
/*
|
|
12
|
-
|
|
13
|
-
If you want to overwrite these in a C2P project, you'll need 3 columns with these names exactly:
|
|
14
|
-
|
|
15
|
-
NewsletterID
|
|
16
|
-
NewsletterPromo
|
|
17
|
-
NewsletterLegal
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const NewsletterSignup = () => {
|
|
22
|
-
const {
|
|
23
|
-
site: {
|
|
24
|
-
siteMetadata: {
|
|
25
|
-
PROJECT: {
|
|
26
|
-
NEWSLETTER_ID, NEWSLETTER_LEGAL, NEWSLETTER_PROMO, MARKET_KEY, SUBFOLDER, SLUG
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
} = useStaticQuery(graphql`
|
|
31
|
-
query {
|
|
32
|
-
site {
|
|
33
|
-
siteMetadata {
|
|
34
|
-
PROJECT {
|
|
35
|
-
SLUG
|
|
36
|
-
SUBFOLDER
|
|
37
|
-
NEWSLETTER_ID
|
|
38
|
-
NEWSLETTER_LEGAL
|
|
39
|
-
NEWSLETTER_PROMO
|
|
40
|
-
MARKET_KEY
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
`)
|
|
46
|
-
|
|
47
|
-
const [email, setEmail] = useState('')
|
|
48
|
-
const [emailValid, setEmailValid] = useState(false)
|
|
49
|
-
const [submitting, setSubmitting] = useState(false)
|
|
50
|
-
const [errorSubmitted, setErrorSubmitted] = useState(false)
|
|
51
|
-
const [signedUp, setSignedUp] = useState(false)
|
|
52
|
-
const [signupError, setSignupError] = useState(false)
|
|
53
|
-
const thisBrand = getBrands(MARKET_KEY);
|
|
54
|
-
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
// Append script after load
|
|
57
|
-
const script = document.createElement('script')
|
|
58
|
-
script.src =
|
|
59
|
-
'https://www.google.com/recaptcha/api.js?render=6LeBOJAUAAAAAPH7JcaZoQpNcXoHz8T6bFjqlxRg'
|
|
60
|
-
script.async = true
|
|
61
|
-
document.body.appendChild(script)
|
|
62
|
-
}, [])
|
|
63
|
-
|
|
64
|
-
return (
|
|
65
|
-
<div className={newsletterStyles.container}>
|
|
66
|
-
<div className={newsletterStyles.wrapper}>
|
|
67
|
-
<div className={newsletterStyles.contentWrapper}>
|
|
68
|
-
<div className={newsletterStyles.content}>
|
|
69
|
-
<div className={newsletterStyles.headshot}>
|
|
70
|
-
<img
|
|
71
|
-
alt="logo"
|
|
72
|
-
src={"https://files.sfchronicle.com/static-assets/logos/"+thisBrand.attributes.marketPrefix+"-square-black.png"}
|
|
73
|
-
/>
|
|
74
|
-
</div>
|
|
75
|
-
<p
|
|
76
|
-
className={newsletterStyles.newsletterHeadline}
|
|
77
|
-
dangerouslySetInnerHTML={{ __html: NEWSLETTER_PROMO }}
|
|
78
|
-
/>
|
|
79
|
-
</div>
|
|
80
|
-
|
|
81
|
-
<div className={newsletterStyles.formWrapper}>
|
|
82
|
-
<div>
|
|
83
|
-
<div className={newsletterStyles.inputWrapper}>
|
|
84
|
-
<input
|
|
85
|
-
className={newsletterStyles.userSignUp}
|
|
86
|
-
value={email}
|
|
87
|
-
onChange={(e) => {
|
|
88
|
-
setEmail(e.currentTarget.value)
|
|
89
|
-
setErrorSubmitted(false)
|
|
90
|
-
}}
|
|
91
|
-
placeholder="Enter your email address here."
|
|
92
|
-
type="email"
|
|
93
|
-
/>
|
|
94
|
-
|
|
95
|
-
<button
|
|
96
|
-
className={newsletterStyles.submitButton}
|
|
97
|
-
type="submit"
|
|
98
|
-
onClick={() =>
|
|
99
|
-
handleSubmit({
|
|
100
|
-
email,
|
|
101
|
-
setEmailValid,
|
|
102
|
-
emailValid,
|
|
103
|
-
setSubmitting,
|
|
104
|
-
setSignedUp,
|
|
105
|
-
setSignupError,
|
|
106
|
-
setErrorSubmitted,
|
|
107
|
-
MARKET_KEY,
|
|
108
|
-
NEWSLETTER_ID
|
|
109
|
-
},
|
|
110
|
-
`${SUBFOLDER}|${SLUG}` //Unique ID for this signup
|
|
111
|
-
)
|
|
112
|
-
}
|
|
113
|
-
>
|
|
114
|
-
{submitting ? (
|
|
115
|
-
<img src="https://projects.sfchronicle.com/shared/logos/loading.gif" />
|
|
116
|
-
) : (
|
|
117
|
-
<span>Sign up</span>
|
|
118
|
-
)}
|
|
119
|
-
</button>
|
|
120
|
-
</div>
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
<div className={newsletterStyles.outputText}>
|
|
126
|
-
{errorSubmitted && (
|
|
127
|
-
<p className={newsletterStyles.red}>
|
|
128
|
-
This email is invalid. Please update and submit again.
|
|
129
|
-
</p>
|
|
130
|
-
)}
|
|
131
|
-
{signupError && (
|
|
132
|
-
<p className={newsletterStyles.red}>
|
|
133
|
-
There was a problem with the server, please try again later.
|
|
134
|
-
</p>
|
|
135
|
-
)}
|
|
136
|
-
{signedUp && <p>Thank you for signing up for our newsletter!</p>}
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<p className={newsletterStyles.disclaimer}>
|
|
140
|
-
This site is protected by reCAPTCHA and the Google{' '}
|
|
141
|
-
<a href="https://policies.google.com/privacy" target="_blank">
|
|
142
|
-
Privacy Policy
|
|
143
|
-
</a>{' '}
|
|
144
|
-
and{' '}
|
|
145
|
-
<a href="https://policies.google.com/terms" target="_blank">
|
|
146
|
-
Terms of Service
|
|
147
|
-
</a>{' '}
|
|
148
|
-
{NEWSLETTER_LEGAL &&
|
|
149
|
-
<span dangerouslySetInnerHTML={{__html: NEWSLETTER_LEGAL}} />
|
|
150
|
-
}
|
|
151
|
-
</p>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
)
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export default NewsletterSignup
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import * as relatedlinkStyles from '../../styles/modules/relatedlink.module.less'
|
|
4
|
-
import WCMImage from './wcmimage'
|
|
5
|
-
|
|
6
|
-
const RelatedLink = ({ url, wcmid, title }) => (
|
|
7
|
-
<div className={relatedlinkStyles.container}>
|
|
8
|
-
<a href={url}>
|
|
9
|
-
{wcmid &&
|
|
10
|
-
<WCMImage wcm={wcmid} alt="Thumbnail image for a related story" ratio={"55%"} />
|
|
11
|
-
}
|
|
12
|
-
<p className={relatedlinkStyles.text}>{title}</p>
|
|
13
|
-
</a>
|
|
14
|
-
</div>
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
RelatedLink.propTypes = {
|
|
18
|
-
url: PropTypes.string,
|
|
19
|
-
wcmid: PropTypes.number,
|
|
20
|
-
title: PropTypes.string,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default RelatedLink
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import RelatedLink from './relatedlink'
|
|
4
|
-
import * as relatedrowStyles from '../../styles/modules/relatedrow.module.less'
|
|
5
|
-
|
|
6
|
-
const RelatedRow = ({ links }) => (
|
|
7
|
-
<div className={relatedrowStyles.links}>
|
|
8
|
-
{links.map((link) => (
|
|
9
|
-
<RelatedLink
|
|
10
|
-
key={link.title}
|
|
11
|
-
url={link.url}
|
|
12
|
-
wcmid={link.wcmid}
|
|
13
|
-
title={link.title}
|
|
14
|
-
/>
|
|
15
|
-
))}
|
|
16
|
-
</div>
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
RelatedRow.propTypes = {
|
|
20
|
-
links: PropTypes.array,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default RelatedRow
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import React, {useState, useEffect} from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import RelatedRow from './relatedrow'
|
|
4
|
-
import * as relatedsectionStyles from '../../styles/modules/relatedsection.module.less'
|
|
5
|
-
import { setRichieParam } from './component-helpers/utilfunctions'
|
|
6
|
-
|
|
7
|
-
const RelatedSection = ({ links }) => {
|
|
8
|
-
let [relLinks, setRelLinks] = useState(links)
|
|
9
|
-
|
|
10
|
-
// Reset in case they need URL params for app
|
|
11
|
-
useEffect(() => {
|
|
12
|
-
setRelLinks(setRichieParam(relLinks))
|
|
13
|
-
}, [])
|
|
14
|
-
|
|
15
|
-
return <section aria-label="Related links" className={relatedsectionStyles.section}>
|
|
16
|
-
<div className={relatedsectionStyles.hed}>Read more</div>
|
|
17
|
-
<RelatedRow links={relLinks} />
|
|
18
|
-
</section>
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
RelatedSection.propTypes = {
|
|
22
|
-
links: PropTypes.array.isRequired,
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default RelatedSection
|
|
26
|
-
|
|
27
|
-
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import React, { Fragment, useState, useEffect } from 'react'
|
|
2
|
-
import { Link } from 'gatsby'
|
|
3
|
-
import PropTypes from 'prop-types'
|
|
4
|
-
import { setRichieParam } from '../sfc/component-helpers/utilfunctions'
|
|
5
|
-
|
|
6
|
-
// Add SFC utils
|
|
7
|
-
import { appCheck } from '../../../../index'
|
|
8
|
-
|
|
9
|
-
// withinProject: enable to get the benefits of Gatsby's speed loading for pages on the same project structure
|
|
10
|
-
// withinSite: disable if this is a link to an outside resource, like a government site or document
|
|
11
|
-
// you can assign other link-related attributes to this component, they'll transfer using the spread operator
|
|
12
|
-
|
|
13
|
-
const SafeLink = ({
|
|
14
|
-
href,
|
|
15
|
-
onClick,
|
|
16
|
-
withinProject = false,
|
|
17
|
-
withinSite = true,
|
|
18
|
-
children,
|
|
19
|
-
...other
|
|
20
|
-
}) => {
|
|
21
|
-
// Track what the link should be
|
|
22
|
-
let [linkURL, setLinkURL] = useState(href)
|
|
23
|
-
|
|
24
|
-
// Check on first load if this is in the app or not
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const isApp = appCheck()
|
|
27
|
-
if (isApp) {
|
|
28
|
-
setLinkURL(setRichieParam(href))
|
|
29
|
-
}
|
|
30
|
-
}, [])
|
|
31
|
-
|
|
32
|
-
// Handle anchor tags for the app
|
|
33
|
-
const interceptClick = (e, onClick) => {
|
|
34
|
-
e.preventDefault()
|
|
35
|
-
const scrollEl = document.getElementById(href.substring(1))
|
|
36
|
-
if (scrollEl) {
|
|
37
|
-
scrollEl.scrollIntoView()
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Preserve and run any original on click functionality
|
|
41
|
-
if (onClick) {
|
|
42
|
-
onClick()
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// If we detect an anchor tag, sub in behavior
|
|
47
|
-
let clickFunc = onClick
|
|
48
|
-
if (href.charAt(0) === '#') {
|
|
49
|
-
clickFunc = interceptClick
|
|
50
|
-
}
|
|
51
|
-
// If we have no function, add a dummy one so we don't error
|
|
52
|
-
if (!clickFunc){
|
|
53
|
-
clickFunc = () => {}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<Fragment>
|
|
58
|
-
{withinProject ? (
|
|
59
|
-
<Link to={linkURL} onClick={(e) => clickFunc(e, onClick)} {...other}>
|
|
60
|
-
{children}
|
|
61
|
-
</Link>
|
|
62
|
-
) : (
|
|
63
|
-
<a
|
|
64
|
-
href={linkURL}
|
|
65
|
-
target={withinSite ? '_self' : '_blank'}
|
|
66
|
-
rel={withinSite ? null : 'noreferrer'}
|
|
67
|
-
onClick={(e) => {
|
|
68
|
-
if (clickFunc){
|
|
69
|
-
clickFunc(e, onClick)
|
|
70
|
-
}
|
|
71
|
-
}}
|
|
72
|
-
{...other}
|
|
73
|
-
>
|
|
74
|
-
{children}
|
|
75
|
-
</a>
|
|
76
|
-
)}
|
|
77
|
-
</Fragment>
|
|
78
|
-
)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
SafeLink.propTypes = {
|
|
82
|
-
href: PropTypes.string.isRequired,
|
|
83
|
-
children: PropTypes.node.isRequired,
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export default SafeLink
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import { scrollToTarget } from './component-helpers/scrolldownhelpers'
|
|
4
|
-
import * as scrolldownStyles from '../../styles/modules/scrolldown.module.less'
|
|
5
|
-
import 'css.gg/icons/css/chevron-down.css'
|
|
6
|
-
|
|
7
|
-
const ScrollDown = ({ scrollTo }) => (
|
|
8
|
-
<div className={scrolldownStyles.arrow}>
|
|
9
|
-
<div
|
|
10
|
-
tabIndex="0"
|
|
11
|
-
role="button"
|
|
12
|
-
className="gg-chevron-down"
|
|
13
|
-
onClick={() => scrollToTarget(scrollTo)}
|
|
14
|
-
></div>
|
|
15
|
-
</div>
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
ScrollDown.propTypes = {
|
|
19
|
-
scrollTo: PropTypes.string.isRequired,
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default ScrollDown
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import * as shareStyles from '../../styles/modules/share.module.less'
|
|
3
|
-
|
|
4
|
-
const ShareButtons = ({ meta, urlAdd }) => {
|
|
5
|
-
// Extension to URL if passed in
|
|
6
|
-
if (!urlAdd) {
|
|
7
|
-
urlAdd = ''
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let facebookClick = (e) => {
|
|
11
|
-
const link = e.currentTarget.getAttribute('href')
|
|
12
|
-
e.preventDefault()
|
|
13
|
-
if (link) {
|
|
14
|
-
window.open(link, 'facebook-share-dialog', 'width=626,height=436')
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
let subfolder = ''
|
|
19
|
-
if (meta.PROJECT.SUBFOLDER) {
|
|
20
|
-
subfolder = meta.PROJECT.SUBFOLDER + '/'
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<div className={shareStyles.wrapper}>
|
|
25
|
-
{/* Twitter */}
|
|
26
|
-
<a
|
|
27
|
-
href={`https://twitter.com/intent/tweet?url=${meta.MAIN_DOMAIN}%2F${subfolder}${meta.PROJECT.SLUG}%2F${urlAdd}&text=${meta.PROJECT.TWITTER_TEXT}`}
|
|
28
|
-
className={shareStyles.link}
|
|
29
|
-
>
|
|
30
|
-
<svg
|
|
31
|
-
className={shareStyles.svg}
|
|
32
|
-
data-icon="twitter"
|
|
33
|
-
role="img"
|
|
34
|
-
aria-hidden="true"
|
|
35
|
-
focusable="false"
|
|
36
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
37
|
-
viewBox="0 0 248 204"
|
|
38
|
-
>
|
|
39
|
-
<path
|
|
40
|
-
data-name="Twitter Logo"
|
|
41
|
-
fill="currentColor"
|
|
42
|
-
d="M222 51.29c.15 2.16.15 4.34.15 6.52 0 66.74-50.8 143.69-143.69 143.69A142.91 142.91 0 0 1 1 178.82a102.72 102.72 0 0 0 12 .72 101.29 101.29 0 0 0 62.72-21.66 50.53 50.53 0 0 1-47.18-35.07 50.35 50.35 0 0 0 22.8-.86 50.53 50.53 0 0 1-40.52-49.5v-.64a50.25 50.25 0 0 0 22.92 6.32 50.55 50.55 0 0 1-15.6-67.42 143.38 143.38 0 0 0 104.08 52.77 50.55 50.55 0 0 1 86.06-46.06 101.19 101.19 0 0 0 32.06-12.26 50.66 50.66 0 0 1-22.2 27.93 100.89 100.89 0 0 0 29-7.94A102.84 102.84 0 0 1 222 51.29z"
|
|
43
|
-
/>
|
|
44
|
-
</svg>
|
|
45
|
-
</a>
|
|
46
|
-
{/* Facebook */}
|
|
47
|
-
<a
|
|
48
|
-
href={`https://www.facebook.com/sharer/sharer.php?u=${meta.MAIN_DOMAIN}%2F${subfolder}${meta.PROJECT.SLUG}%2F${urlAdd}`}
|
|
49
|
-
className={shareStyles.link}
|
|
50
|
-
onClick={facebookClick}
|
|
51
|
-
>
|
|
52
|
-
<svg
|
|
53
|
-
className={shareStyles.svg}
|
|
54
|
-
aria-hidden="true"
|
|
55
|
-
focusable="false"
|
|
56
|
-
data-prefix="fab"
|
|
57
|
-
data-icon="facebook"
|
|
58
|
-
role="img"
|
|
59
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
60
|
-
viewBox="0 0 512 512"
|
|
61
|
-
>
|
|
62
|
-
<path
|
|
63
|
-
fill="currentColor"
|
|
64
|
-
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
|
|
65
|
-
></path>
|
|
66
|
-
</svg>
|
|
67
|
-
</a>
|
|
68
|
-
{/* Email */}
|
|
69
|
-
<a
|
|
70
|
-
href={`mailto:?subject=${meta.PROJECT.TITLE}&body=${meta.PROJECT.DESCRIPTION}%0A%0A${meta.MAIN_DOMAIN}%2F${subfolder}${meta.PROJECT.SLUG}%2F${urlAdd}`}
|
|
71
|
-
className={shareStyles.link}
|
|
72
|
-
>
|
|
73
|
-
<svg
|
|
74
|
-
className={shareStyles.svg}
|
|
75
|
-
aria-hidden="true"
|
|
76
|
-
focusable="false"
|
|
77
|
-
data-prefix="fas"
|
|
78
|
-
data-icon="envelope"
|
|
79
|
-
role="img"
|
|
80
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
81
|
-
viewBox="0 0 512 512"
|
|
82
|
-
>
|
|
83
|
-
<path
|
|
84
|
-
fill="currentColor"
|
|
85
|
-
d="M502.3 190.8c3.9-3.1 9.7-.2 9.7 4.7V400c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V195.6c0-5 5.7-7.8 9.7-4.7 22.4 17.4 52.1 39.5 154.1 113.6 21.1 15.4 56.7 47.8 92.2 47.6 35.7.3 72-32.8 92.3-47.6 102-74.1 131.6-96.3 154-113.7zM256 320c23.2.4 56.6-29.2 73.4-41.4 132.7-96.3 142.8-104.7 173.4-128.7 5.8-4.5 9.2-11.5 9.2-18.9v-19c0-26.5-21.5-48-48-48H48C21.5 64 0 85.5 0 112v19c0 7.4 3.4 14.3 9.2 18.9 30.6 23.9 40.7 32.4 173.4 128.7 16.8 12.2 50.2 41.8 73.4 41.4z"
|
|
86
|
-
></path>
|
|
87
|
-
</svg>
|
|
88
|
-
</a>
|
|
89
|
-
</div>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export default ShareButtons
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import React, { Fragment } from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import Byline from './byline'
|
|
4
|
-
import NavTop from './navtop'
|
|
5
|
-
import ScrollDown from './scrolldown'
|
|
6
|
-
import {
|
|
7
|
-
pubdateString,
|
|
8
|
-
moddateString,
|
|
9
|
-
} from './component-helpers/datehelpers'
|
|
10
|
-
import * as topperStyles from '../../styles/modules/topper.module.less'
|
|
11
|
-
import WCMImage from './wcmimage'
|
|
12
|
-
|
|
13
|
-
const Topper = ({ meta }) => {
|
|
14
|
-
const {
|
|
15
|
-
PROJECT: { AUTHORS, ISO_MODDATE, ISO_PUBDATE, DISPLAY_TITLE, DECK },
|
|
16
|
-
} = meta
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<Fragment>
|
|
20
|
-
<NavTop meta={meta} />
|
|
21
|
-
<header className={topperStyles.header}>
|
|
22
|
-
<section
|
|
23
|
-
aria-label="Topper headline, deck, byline and dateline"
|
|
24
|
-
className={topperStyles.textContainer}
|
|
25
|
-
>
|
|
26
|
-
<h1 className={topperStyles.hed}>{DISPLAY_TITLE ? DISPLAY_TITLE : "A brave new template v2"}</h1>
|
|
27
|
-
<h2 className={topperStyles.dek}>
|
|
28
|
-
{DECK ? DECK : "Words can be like X-rays if you use them properly — they'll go through anything."}
|
|
29
|
-
</h2>
|
|
30
|
-
<div className={topperStyles.dateby}>
|
|
31
|
-
<span>By</span>
|
|
32
|
-
{AUTHORS.map((author, index) => {
|
|
33
|
-
// Pass special flag if this is the last item
|
|
34
|
-
let isLast = false
|
|
35
|
-
if (index === AUTHORS.length - 1) {
|
|
36
|
-
isLast = true
|
|
37
|
-
}
|
|
38
|
-
// Add the bylines
|
|
39
|
-
return (
|
|
40
|
-
<Byline
|
|
41
|
-
key={author.AUTHOR_NAME}
|
|
42
|
-
url={author.AUTHOR_PAGE}
|
|
43
|
-
name={author.AUTHOR_NAME}
|
|
44
|
-
index={index}
|
|
45
|
-
isLast={isLast}
|
|
46
|
-
/>
|
|
47
|
-
)
|
|
48
|
-
})}
|
|
49
|
-
|
|
|
50
|
-
<time
|
|
51
|
-
className="topper-dateline"
|
|
52
|
-
dateTime={ISO_PUBDATE}
|
|
53
|
-
itemProp="datePublished"
|
|
54
|
-
>
|
|
55
|
-
{pubdateString}
|
|
56
|
-
</time>
|
|
57
|
-
{moddateString && (
|
|
58
|
-
<Fragment>
|
|
59
|
-
|
|
|
60
|
-
<time
|
|
61
|
-
className="topper-dateline updated-date"
|
|
62
|
-
dateTime={ISO_MODDATE}
|
|
63
|
-
itemProp="dateModified"
|
|
64
|
-
>
|
|
65
|
-
Updated: {moddateString}
|
|
66
|
-
</time>
|
|
67
|
-
</Fragment>
|
|
68
|
-
)}
|
|
69
|
-
</div>
|
|
70
|
-
</section>
|
|
71
|
-
</header>
|
|
72
|
-
<figure className={topperStyles.figure}>
|
|
73
|
-
{/* You can also use a 100vh/100vw with a normal img tag here, just make sure to set the container that way so we don't get layout shift */}
|
|
74
|
-
<WCMImage wcm={20374215} alt="TKTKTK" lz={false} />
|
|
75
|
-
|
|
76
|
-
{/* This prop gives the arrow an id to scroll to */}
|
|
77
|
-
<ScrollDown scrollTo="article" />
|
|
78
|
-
<figcaption className={topperStyles.topcap}>A man sitting on a hill with the Golden Gate Bridge in the background <span className={topperStyles.topcred}>(Scott Strazzante / San Francisco Chronicle)</span></figcaption>
|
|
79
|
-
</figure>
|
|
80
|
-
</Fragment>
|
|
81
|
-
)
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
Topper.propTypes = {
|
|
85
|
-
meta: PropTypes.object.isRequired,
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export default Topper
|