sfc-utils 1.4.203 → 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/components/helpers/utilfunctions.mjs +17 -24
- 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,40 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import * as buttonStyles from '../../styles/modules/button.module.less'
|
|
4
|
-
|
|
5
|
-
const Button = ({ click, type, text, style, className, name }) => {
|
|
6
|
-
const buttonType = () => {
|
|
7
|
-
switch (style) {
|
|
8
|
-
case 'primary':
|
|
9
|
-
return buttonStyles.btnPrimary
|
|
10
|
-
case 'secondary':
|
|
11
|
-
return buttonStyles.btnSecondary
|
|
12
|
-
case 'disabled':
|
|
13
|
-
return buttonStyles.btnDisabled
|
|
14
|
-
default:
|
|
15
|
-
return buttonStyles.btnPrimary
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return (
|
|
20
|
-
<button
|
|
21
|
-
name={name ? name : ""}
|
|
22
|
-
onClick={click}
|
|
23
|
-
type={type}
|
|
24
|
-
className={className ? `${className} ${buttonType()}` : buttonType()}
|
|
25
|
-
>
|
|
26
|
-
{text}
|
|
27
|
-
</button>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
Button.propTypes = {
|
|
32
|
-
click: PropTypes.func,
|
|
33
|
-
text: PropTypes.string,
|
|
34
|
-
className: PropTypes.string,
|
|
35
|
-
name: PropTypes.string,
|
|
36
|
-
type: PropTypes.string,
|
|
37
|
-
style: PropTypes.oneOf(['primary', 'secondary', 'disabled']).isRequired,
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export default Button
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import React, { Fragment } from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import * as bylineStyles from '../../styles/modules/byline.module.less'
|
|
4
|
-
|
|
5
|
-
const Byline = ({ url, name, index, isLast }) => {
|
|
6
|
-
let prefix = ' '
|
|
7
|
-
// Add necessary spacing and grammar
|
|
8
|
-
if (index > 0) {
|
|
9
|
-
prefix = ', '
|
|
10
|
-
|
|
11
|
-
if (isLast) {
|
|
12
|
-
prefix = ' and '
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return (
|
|
17
|
-
<Fragment>
|
|
18
|
-
{url ? (
|
|
19
|
-
<Fragment>
|
|
20
|
-
{prefix}
|
|
21
|
-
<a target="_blank" rel="author noopener noreferrer" href={url}>
|
|
22
|
-
<span className={bylineStyles.byline}>{name}</span>
|
|
23
|
-
</a>
|
|
24
|
-
</Fragment>
|
|
25
|
-
) : (
|
|
26
|
-
<span>
|
|
27
|
-
{prefix}
|
|
28
|
-
{name}
|
|
29
|
-
</span>
|
|
30
|
-
)}
|
|
31
|
-
</Fragment>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
Byline.propTypes = {
|
|
36
|
-
url: PropTypes.string.isRequired,
|
|
37
|
-
name: PropTypes.string.isRequired,
|
|
38
|
-
index: PropTypes.number,
|
|
39
|
-
isLast: PropTypes.bool,
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export default Byline
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react'
|
|
2
|
-
|
|
3
|
-
function useCanNativeLazyLoad() {
|
|
4
|
-
const [canNativeLazyload, setCanNativeLazyload] = useState('pending')
|
|
5
|
-
|
|
6
|
-
useEffect(() => {
|
|
7
|
-
if ('loading' in HTMLImageElement.prototype) {
|
|
8
|
-
setCanNativeLazyload(true)
|
|
9
|
-
} else {
|
|
10
|
-
setCanNativeLazyload(false)
|
|
11
|
-
}
|
|
12
|
-
}, [])
|
|
13
|
-
|
|
14
|
-
return canNativeLazyload
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export { useCanNativeLazyLoad }
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// Add SFC utils
|
|
3
|
-
import { getSettings } from '../../../../tempsettings'
|
|
4
|
-
const settings = getSettings()
|
|
5
|
-
|
|
6
|
-
// declare date function variables
|
|
7
|
-
// Convert date to readable time
|
|
8
|
-
const readablePubDate = convertDatesToAP(settings.PROJECT.DATE)
|
|
9
|
-
// Check safely for MOD_DATE
|
|
10
|
-
let readableModDate
|
|
11
|
-
// Convert date string to AP style abbreviations
|
|
12
|
-
let pubdateString = readablePubDate
|
|
13
|
-
let moddateString = ''
|
|
14
|
-
|
|
15
|
-
function convertDatesToAP(dateString) {
|
|
16
|
-
// Convert date string to AP style abbreviations
|
|
17
|
-
let newDateString = dateString
|
|
18
|
-
newDateString = newDateString
|
|
19
|
-
.replace('January', 'Jan.')
|
|
20
|
-
.replace('February', 'Feb.')
|
|
21
|
-
.replace('August', 'Aug.')
|
|
22
|
-
.replace('September', 'Sept.')
|
|
23
|
-
.replace('October', 'Oct.')
|
|
24
|
-
.replace('November', 'Nov.')
|
|
25
|
-
.replace('December', 'Dec.')
|
|
26
|
-
.replace('AM', 'a.m.')
|
|
27
|
-
.replace('PM', 'p.m.')
|
|
28
|
-
// Return the result
|
|
29
|
-
return newDateString
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
;(function prepDates() {
|
|
33
|
-
if (typeof settings.PROJECT.MOD_DATE !== 'undefined') {
|
|
34
|
-
readableModDate = convertDatesToAP(settings.PROJECT.MOD_DATE)
|
|
35
|
-
} else {
|
|
36
|
-
// If MOD_DATE does not exist, set false so it doesn't render
|
|
37
|
-
readableModDate = false
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Only check moddate if we have a value
|
|
41
|
-
if (readableModDate) {
|
|
42
|
-
moddateString = readableModDate
|
|
43
|
-
|
|
44
|
-
// Chop time off pubdate if possible
|
|
45
|
-
try {
|
|
46
|
-
// eslint-disable-next-line prefer-destructuring
|
|
47
|
-
pubdateString = readablePubDate.match(/.*\d{4}/gm)[0]
|
|
48
|
-
} catch (err) {
|
|
49
|
-
// That's fine
|
|
50
|
-
console.log(err)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
})()
|
|
54
|
-
|
|
55
|
-
export { pubdateString, moddateString }
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
|
|
3
|
-
// Check the vars to make sure the form is in a state to be sent
|
|
4
|
-
function checkSubmitStatus({ email, setEmailValid, emailValid }) {
|
|
5
|
-
// Check if email is formatted like an email
|
|
6
|
-
if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email)) {
|
|
7
|
-
// Valid email
|
|
8
|
-
setEmailValid(true)
|
|
9
|
-
} else {
|
|
10
|
-
// Invalid email
|
|
11
|
-
setEmailValid(false)
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Check all values
|
|
16
|
-
if (email) {
|
|
17
|
-
// Tests passed! Return true
|
|
18
|
-
return true
|
|
19
|
-
}
|
|
20
|
-
// If tests were not passed, return false
|
|
21
|
-
return false
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Run our submission through reCAPTCHA logic
|
|
25
|
-
function handleSubmit({
|
|
26
|
-
email,
|
|
27
|
-
setEmailValid,
|
|
28
|
-
emailValid,
|
|
29
|
-
setSubmitting,
|
|
30
|
-
setSignedUp,
|
|
31
|
-
setSignupError,
|
|
32
|
-
setErrorSubmitted,
|
|
33
|
-
MARKET_KEY,
|
|
34
|
-
NEWSLETTER_ID
|
|
35
|
-
}, uniqueID) {
|
|
36
|
-
// See if we're able to submit
|
|
37
|
-
if (checkSubmitStatus({ email, setEmailValid, emailValid })) {
|
|
38
|
-
// Change the button graphic to show we're doing something
|
|
39
|
-
setSubmitting(true)
|
|
40
|
-
// Go ahead with the submit
|
|
41
|
-
if (typeof window !== 'undefined') {
|
|
42
|
-
window.grecaptcha.ready(() => {
|
|
43
|
-
// This will fail on mobile because reCAPTCHA does not work on dev domains
|
|
44
|
-
window.grecaptcha
|
|
45
|
-
.execute('6LeBOJAUAAAAAPH7JcaZoQpNcXoHz8T6bFjqlxRg', {
|
|
46
|
-
action: 'newslettersignup',
|
|
47
|
-
})
|
|
48
|
-
.then((token) => {
|
|
49
|
-
// Append write-in prefix if this is a write-in answer
|
|
50
|
-
axios
|
|
51
|
-
.post(
|
|
52
|
-
'https://projects.sfchronicle.com/feeds/voting/index.php',
|
|
53
|
-
{
|
|
54
|
-
token: token,
|
|
55
|
-
formData: {
|
|
56
|
-
// Sending data that jives with what PHP expects
|
|
57
|
-
email: email,
|
|
58
|
-
pageChoices: [],
|
|
59
|
-
newsletterSignup: true,
|
|
60
|
-
customSailID: NEWSLETTER_ID,
|
|
61
|
-
uniqueID: uniqueID,
|
|
62
|
-
logSource: '*EMBEDDED SIGNUP*',
|
|
63
|
-
marketKey: MARKET_KEY
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
{ headers: { 'Content-Type': 'application/json' } }
|
|
67
|
-
)
|
|
68
|
-
.then((response) => {
|
|
69
|
-
// If successful, show end of form
|
|
70
|
-
if (response.status === 200) {
|
|
71
|
-
setSignedUp(true)
|
|
72
|
-
setSubmitting(false)
|
|
73
|
-
} else {
|
|
74
|
-
// Show error if our server fails
|
|
75
|
-
setSignupError(true)
|
|
76
|
-
setSubmitting(false)
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
return true
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
} else {
|
|
84
|
-
// Submit error! Change state to expose error help text
|
|
85
|
-
setErrorSubmitted(true)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export { handleSubmit }
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
// Grabs a param from the URL structure
|
|
3
|
-
function getParameterByName(name, url = window.location.href) {
|
|
4
|
-
name = name.replace(/[\[\]]/g, '\\$&');
|
|
5
|
-
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
|
|
6
|
-
results = regex.exec(url);
|
|
7
|
-
if (!results) return null;
|
|
8
|
-
if (!results[2]) return '';
|
|
9
|
-
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function convertURL(url){
|
|
13
|
-
// Check if we already have a ?
|
|
14
|
-
let newURL = url
|
|
15
|
-
let paramConcat = "&"
|
|
16
|
-
if (newURL.indexOf("?") === -1){
|
|
17
|
-
paramConcat = "?"
|
|
18
|
-
}
|
|
19
|
-
newURL += paramConcat + "fromRichie=1"
|
|
20
|
-
return newURL
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Adds Richie param to an array of links
|
|
24
|
-
function setRichieParam(linksArray){
|
|
25
|
-
if (!linksArray){
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
if (typeof window !== "undefined"){
|
|
29
|
-
let param = getParameterByName("fromRichie")
|
|
30
|
-
|
|
31
|
-
// Check if we're dealing with a string
|
|
32
|
-
if (typeof linksArray == "string"){
|
|
33
|
-
if (param){
|
|
34
|
-
let newURL = convertURL(linksArray)
|
|
35
|
-
return newURL
|
|
36
|
-
} else {
|
|
37
|
-
return linksArray
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Otherwise, assume array
|
|
42
|
-
let newArray = linksArray.slice()
|
|
43
|
-
if (param){
|
|
44
|
-
// If we found the param, loop through and add it to all the section links
|
|
45
|
-
for (let item in newArray){
|
|
46
|
-
let thisItem = newArray[item]
|
|
47
|
-
thisItem.url = convertURL(thisItem.url)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return newArray
|
|
51
|
-
} else {
|
|
52
|
-
// If window is undefined, return empty
|
|
53
|
-
return []
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function debounce(fn, ms) {
|
|
58
|
-
let timer
|
|
59
|
-
return _ => {
|
|
60
|
-
clearTimeout(timer)
|
|
61
|
-
timer = setTimeout(_ => {
|
|
62
|
-
timer = null
|
|
63
|
-
fn.apply(this, arguments)
|
|
64
|
-
}, ms)
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export { getParameterByName, setRichieParam, debounce }
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import * as creditlineStyles from '../../styles/modules/creditline.module.less'
|
|
4
|
-
|
|
5
|
-
const CreditLine = ({ name, email, twitter }) => (
|
|
6
|
-
<p className={creditlineStyles.text}>
|
|
7
|
-
{name}
|
|
8
|
-
{email && (
|
|
9
|
-
<span>
|
|
10
|
-
• <a href={`mailto:${email} `}>{email.toLowerCase()}</a>{' '}
|
|
11
|
-
</span>
|
|
12
|
-
)}
|
|
13
|
-
{twitter && (
|
|
14
|
-
<span>
|
|
15
|
-
•{' '}
|
|
16
|
-
<a
|
|
17
|
-
className={creditlineStyles.link}
|
|
18
|
-
target="_blank"
|
|
19
|
-
rel="noopener noreferrer"
|
|
20
|
-
href={`https://twitter.com/${twitter} `}
|
|
21
|
-
>
|
|
22
|
-
@{twitter}
|
|
23
|
-
</a>
|
|
24
|
-
</span>
|
|
25
|
-
)}
|
|
26
|
-
</p>
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
CreditLine.propTypes = {
|
|
30
|
-
name: PropTypes.string.isRequired,
|
|
31
|
-
email: PropTypes.string,
|
|
32
|
-
twitter: PropTypes.string,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export default CreditLine
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import * as creditsStyles from '../../styles/modules/credits.module.less'
|
|
4
|
-
|
|
5
|
-
const Credits = ({ type, children }) => (
|
|
6
|
-
<>
|
|
7
|
-
<p className={creditsStyles.title}>{type}</p>
|
|
8
|
-
{children}
|
|
9
|
-
</>
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
Credits.propTypes = {
|
|
13
|
-
type: PropTypes.string.isRequired,
|
|
14
|
-
children: PropTypes.node.isRequired,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default Credits
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import Credits from './credits'
|
|
3
|
-
import CreditLine from './creditline'
|
|
4
|
-
import OgPubDate from '../../../../components/ogpubdate.mjs'
|
|
5
|
-
import * as creditssectionStyles from '../../styles/modules/creditssection.module.less'
|
|
6
|
-
let rawCredits;
|
|
7
|
-
try {
|
|
8
|
-
rawCredits = require('../../data/credits.sheet.json')
|
|
9
|
-
} catch (err){
|
|
10
|
-
// It's fine
|
|
11
|
-
rawCredits = null;
|
|
12
|
-
}
|
|
13
|
-
const CreditsSection = () => {
|
|
14
|
-
//get rid of instructions
|
|
15
|
-
let credits = rawCredits
|
|
16
|
-
|
|
17
|
-
// Put credits into structured array
|
|
18
|
-
let creditObj = {}
|
|
19
|
-
if(credits){
|
|
20
|
-
credits.forEach((credit) => {
|
|
21
|
-
if (typeof creditObj[credit.role] === 'undefined') {
|
|
22
|
-
// Create an empty array with key of role
|
|
23
|
-
creditObj[credit.role] = []
|
|
24
|
-
}
|
|
25
|
-
// Push into role array regardless
|
|
26
|
-
creditObj[credit.role].push(credit)
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
return (
|
|
30
|
-
<section aria-label="Credits" className={creditssectionStyles.section}>
|
|
31
|
-
<OgPubDate/>
|
|
32
|
-
<h4 className={creditssectionStyles.hed}>Credits</h4>
|
|
33
|
-
{Object.keys(creditObj).map((key) => (
|
|
34
|
-
<Credits type={key} key={key}>
|
|
35
|
-
{creditObj[key].map((credit) => (
|
|
36
|
-
<CreditLine
|
|
37
|
-
key={credit.name}
|
|
38
|
-
name={credit.name}
|
|
39
|
-
email={credit.email}
|
|
40
|
-
twitter={credit.twitter}
|
|
41
|
-
/>
|
|
42
|
-
))}
|
|
43
|
-
</Credits>
|
|
44
|
-
))}
|
|
45
|
-
</section>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default CreditsSection
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
import * as dropcapStyles from '../../styles/modules/dropcap.module.less'
|
|
4
|
-
|
|
5
|
-
const DropCap = ({ children }) => (
|
|
6
|
-
<span className={dropcapStyles.dropcap}>
|
|
7
|
-
{children}
|
|
8
|
-
</span>
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
DropCap.propTypes = {
|
|
12
|
-
children: PropTypes.node.isRequired,
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export default DropCap
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import React, {useState, useEffect} from 'react'
|
|
2
|
-
import { getFooter } from '../../../../index'
|
|
3
|
-
// simple scroll-to function package https://github.com/callmecavs/jump.js
|
|
4
|
-
import jump from 'jump.js'
|
|
5
|
-
import * as navStyles from '../../styles/footer.less'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const Footer = ({meta}) => {
|
|
9
|
-
//If this is CT, Texcom, or Midcom, we need to reset state
|
|
10
|
-
let startingHTML = "";
|
|
11
|
-
if (['Texcom','Midcom','CT'].indexOf(meta.PROJECT.MARKET_KEY) === -1) {
|
|
12
|
-
startingHTML = getFooter(meta, true)
|
|
13
|
-
}
|
|
14
|
-
let [footerHTML, setFooterHTML] = useState(startingHTML)
|
|
15
|
-
|
|
16
|
-
// grabs footer return to top button and makes it jump to top on click
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
let footerTopButton = document.querySelector('#scrollTop')
|
|
19
|
-
if (footerTopButton){
|
|
20
|
-
footerTopButton.onclick = () => jump('#___gatsby', { a11y: true })
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
setFooterHTML(getFooter(meta, true))
|
|
24
|
-
}, [])
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
/*
|
|
28
|
-
getFooter params:
|
|
29
|
-
1. meta
|
|
30
|
-
2. invert colors (boolean)
|
|
31
|
-
*/
|
|
32
|
-
<div dangerouslySetInnerHTML={{__html: footerHTML}} />
|
|
33
|
-
)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export default Footer
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import React, { Component } from 'react'
|
|
2
|
-
import axios from 'axios'
|
|
3
|
-
import * as geocoderStyles from '../../styles/modules/geocoder.module.less'
|
|
4
|
-
|
|
5
|
-
/* Simple Geocoder using the Census API
|
|
6
|
-
* If this.props.returnFunc exists, call it with the result of the query
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
export default class Geocoder extends Component {
|
|
10
|
-
constructor(props) {
|
|
11
|
-
super(props)
|
|
12
|
-
|
|
13
|
-
this.state = {
|
|
14
|
-
searchVal: '',
|
|
15
|
-
searching: false,
|
|
16
|
-
results: {
|
|
17
|
-
result: null,
|
|
18
|
-
},
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
sendResults(coords) {
|
|
23
|
-
if (this.props.resultFunc) {
|
|
24
|
-
this.props.resultFunc(coords)
|
|
25
|
-
}
|
|
26
|
-
// Clear results on click
|
|
27
|
-
this.setState({
|
|
28
|
-
results: {
|
|
29
|
-
result: null,
|
|
30
|
-
},
|
|
31
|
-
})
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
onKeyPress(e) {
|
|
35
|
-
// Clear results on key press
|
|
36
|
-
this.setState({
|
|
37
|
-
results: {
|
|
38
|
-
result: null,
|
|
39
|
-
},
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
if (e.key === 'Enter') {
|
|
43
|
-
this.searchAddress()
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
searchAddress() {
|
|
48
|
-
if (this.state.searching === false) {
|
|
49
|
-
let geocodeURL = 'https://projects.sfchronicle.com/feeds/geocode/'
|
|
50
|
-
this.setState({
|
|
51
|
-
searching: true,
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
// UNCOMMENT THIS SET STATE TO WORK WITH A TEST RESULT
|
|
55
|
-
/*
|
|
56
|
-
this.setState({
|
|
57
|
-
results: {
|
|
58
|
-
"result": {
|
|
59
|
-
"addressMatches": [
|
|
60
|
-
{
|
|
61
|
-
"matchedAddress": "901 MISSION TEST TEST TEST",
|
|
62
|
-
"coordinates": {
|
|
63
|
-
"x": -122.4066217,
|
|
64
|
-
"y": 37.7822158
|
|
65
|
-
},
|
|
66
|
-
}
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
// NOTE: This will fail on localhost for CORS reasons, trust it to work on the server
|
|
74
|
-
axios.post(geocodeURL, { query: this.state.searchVal }).then((result) => {
|
|
75
|
-
this.setState({
|
|
76
|
-
results: result.data,
|
|
77
|
-
searching: false,
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
render() {
|
|
84
|
-
// Check for validity of data
|
|
85
|
-
let validAddresses = null
|
|
86
|
-
try {
|
|
87
|
-
validAddresses = this.state.results.result.addressMatches
|
|
88
|
-
} catch (err) {
|
|
89
|
-
// This is ok, it just means endpoint was not available
|
|
90
|
-
if (this.state.results.result !== null) {
|
|
91
|
-
validAddresses = false
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
<div className={geocoderStyles.wrapper}>
|
|
97
|
-
<div className={geocoderStyles.addressInput}>
|
|
98
|
-
<input
|
|
99
|
-
type="address"
|
|
100
|
-
value={this.state.searchVal}
|
|
101
|
-
onKeyPress={this.onKeyPress.bind(this)}
|
|
102
|
-
placeholder="901 Mission St, San Francisco"
|
|
103
|
-
onChange={(e) => {
|
|
104
|
-
this.setState({
|
|
105
|
-
searchVal: e.currentTarget.value,
|
|
106
|
-
})
|
|
107
|
-
}}
|
|
108
|
-
/>
|
|
109
|
-
<button type="button" onClick={this.searchAddress.bind(this)}>
|
|
110
|
-
{!this.state.searching ? (
|
|
111
|
-
<span>Search</span>
|
|
112
|
-
) : (
|
|
113
|
-
<img src="https://projects.sfchronicle.com/shared/logos/spinner.gif" />
|
|
114
|
-
)}
|
|
115
|
-
</button>
|
|
116
|
-
</div>
|
|
117
|
-
{validAddresses === false && (
|
|
118
|
-
<div className={geocoderStyles.resultBox}>
|
|
119
|
-
<div className={`${geocoderStyles.result} ${geocoderStyles.empty}`}>
|
|
120
|
-
Something went wrong, please try again later
|
|
121
|
-
</div>
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
{validAddresses && validAddresses.length === 0 && (
|
|
125
|
-
<div className={geocoderStyles.resultBox}>
|
|
126
|
-
<div className={`${geocoderStyles.result} ${geocoderStyles.empty}`}>
|
|
127
|
-
No matches, please add more detail
|
|
128
|
-
</div>
|
|
129
|
-
</div>
|
|
130
|
-
)}
|
|
131
|
-
{validAddresses && validAddresses.length > 0 && (
|
|
132
|
-
<div className={geocoderStyles.resultBox}>
|
|
133
|
-
{this.state.results.result.addressMatches.map((item, index) => {
|
|
134
|
-
return (
|
|
135
|
-
<div
|
|
136
|
-
className={geocoderStyles.result}
|
|
137
|
-
onClick={() => this.sendResults(item.coordinates)}
|
|
138
|
-
>
|
|
139
|
-
{item.matchedAddress}
|
|
140
|
-
</div>
|
|
141
|
-
)
|
|
142
|
-
})}
|
|
143
|
-
</div>
|
|
144
|
-
)}
|
|
145
|
-
</div>
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import PropTypes from 'prop-types'
|
|
3
|
-
|
|
4
|
-
const MiscCredit = ({ link, text }) => (
|
|
5
|
-
<p>
|
|
6
|
-
<a href={link} rel="noopener noreferrer" target="_blank">
|
|
7
|
-
{text}
|
|
8
|
-
</a>
|
|
9
|
-
</p>
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
MiscCredit.propTypes = {
|
|
13
|
-
link: PropTypes.string,
|
|
14
|
-
text: PropTypes.string,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export default MiscCredit
|