Created
September 26, 2022 14:32
-
-
Save rickiesmooth/1198a80d6305b3ffd9d6e0c4458a25bb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { addPropertyControls, ControlType, RenderTarget, withCSS } from "framer" | |
import { HTMLMotionProps, motion } from "framer-motion" | |
import * as React from "react" | |
import { | |
containerStyles, | |
usePadding, | |
useRadius, | |
paddingControl, | |
borderRadiusControl, | |
fontControls, | |
useFontControls, | |
} from "https://framer.com/m/framer/default-utils.js@^0.45.0" | |
import { | |
ComponentType, | |
CSSProperties, | |
useCallback, | |
useMemo, | |
useState, | |
} from "react" | |
interface Props extends Omit<HTMLMotionProps<"div">, "layout"> { | |
formId: string | |
withName: boolean | |
withEmail: boolean | |
withMessage: boolean | |
withTwitterHandle: boolean | |
nameField: any | |
email: any | |
message: any | |
twitterHandle: any | |
layout: "horizontal" | "vertical" | |
inputs: any | |
button: any | |
gap: number | |
style: CSSProperties | |
onSubmit?: () => void | |
} | |
const emailRegex = | |
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ | |
const validateEmail = (email) => { | |
return emailRegex.test(String(email).toLowerCase()) | |
} | |
/** | |
* FORMSPARK | |
* | |
* @framerIntrinsicWidth 550 | |
* @framerIntrinsicHeight 290 | |
* | |
* @framerSupportedLayoutWidth fixed | |
* @framerSupportedLayoutHeight fixed | |
*/ | |
const FormSparkWithTwitterHandle: ComponentType<Props> = withCSS<Props>( | |
function FormSparkWithTwitterHandle({ | |
formId, | |
withName, | |
nameField: name, | |
withEmail, | |
email, | |
withMessage, | |
message, | |
withTwitterHandle, | |
twitterHandle, | |
layout, | |
inputs, | |
button, | |
style, | |
gap, | |
onSubmit, | |
...props | |
}) { | |
const [nameValue, setName] = useState(name?.value) | |
const [emailValue, setEmail] = useState(email?.value) | |
const [messageValue, setMessage] = useState(message?.value) | |
const [twitterHandleValue, setTwitterHandle] = useState(message?.value) | |
const [isNameError, setNameError] = useState(false) | |
const [isEmailError, setEmailError] = useState(false) | |
const [isMessageError, setMessageError] = useState(false) | |
const [isTwitterHandleError, setTwitterHandleError] = useState(false) | |
const [isLoading, setLoading] = useState(false) | |
const [isSuccess, setSuccess] = useState(false) | |
const isCanvas = useMemo(() => { | |
return RenderTarget.current() === RenderTarget.canvas | |
}, []) | |
const gridTemplateRows = useMemo(() => { | |
const rows = [] | |
if (withName || withMessage || withTwitterHandle) { | |
rows.push("max-content") | |
} | |
if (withMessage) { | |
rows.push("1fr") | |
} | |
return [...rows, "max-content"].join(" ") | |
}, [withName, withEmail, withMessage, withTwitterHandle]) | |
const gridTemplateColumns = useMemo(() => { | |
const cols = [] | |
if ( | |
((withName && !withEmail) || (withEmail && !withName)) && | |
!withMessage && | |
layout === "horizontal" | |
) { | |
return "1fr max-content" | |
} | |
return "1fr" | |
}, [withName, withEmail, withMessage, withTwitterHandle, layout]) | |
const { fontFamily, fontSize, fontWeight } = useFontControls(props) | |
const borderRadius = useRadius(props) | |
const paddingValue = usePadding(props) | |
const validateForm = useCallback(() => { | |
let error = false | |
setNameError(false) | |
setEmailError(false) | |
setMessageError(false) | |
setTwitterHandleError(false) | |
if (withName && !nameValue) { | |
setNameError(true) | |
error = true | |
} | |
if (withEmail && (!emailValue || !validateEmail(emailValue))) { | |
setEmailError(true) | |
error = true | |
} | |
if (withMessage && !messageValue) { | |
setMessageError(true) | |
error = true | |
} | |
if (withTwitterHandle && !twitterHandleValue) { | |
console.log("twitter error") | |
setTwitterHandleError(true) | |
error = true | |
} | |
return error | |
}, [ | |
validateEmail, | |
withName, | |
withEmail, | |
withMessage, | |
withTwitterHandle, | |
nameValue, | |
emailValue, | |
messageValue, | |
twitterHandleValue, | |
]) | |
const handleSubmit = useCallback( | |
(event: any) => { | |
setLoading(true) | |
event.preventDefault() | |
if (validateForm()) { | |
setLoading(false) | |
} else { | |
const data = new FormData(event.target) | |
const entries = Object.fromEntries(data.entries()) | |
fetch(`https://submit-form.com/${formId}`, { | |
method: "POST", | |
headers: { | |
"Content-Type": "application/json", | |
Accept: "application/json", | |
}, | |
body: JSON.stringify(entries), | |
}) | |
.then(() => { | |
setSuccess(true) | |
onSubmit() | |
}) | |
.catch(() => setLoading(false)) | |
} | |
}, | |
[formId, onSubmit, validateForm] | |
) | |
const handleNameChange = useCallback((event: any) => { | |
setNameError(false) | |
setName(event.target.value) | |
}, []) | |
const handleEmailChange = useCallback((event: any) => { | |
setEmailError(false) | |
setEmail(event.target.value) | |
}, []) | |
const handleMessageChange = useCallback((event: any) => { | |
setMessageError(false) | |
setMessage(event.target.value) | |
}, []) | |
const handleTwitterHandleChange = useCallback((event: any) => { | |
setTwitterHandleError(false) | |
setTwitterHandle(event.target.value) | |
}, []) | |
return ( | |
<motion.div | |
style={{ | |
...style, | |
...containerStyles, | |
flexDirection: "column", | |
"--framer-formspark-placeholder-color": | |
inputs.placeholderColor, | |
}} | |
> | |
{isSuccess ? ( | |
<motion.div | |
style={{ | |
height: "60px", | |
width: "60px", | |
background: button.fill, | |
color: button.color, | |
borderRadius: "50%", | |
display: "flex", | |
justifyContent: "center", | |
alignItems: "center", | |
}} | |
initial={{ scale: 0 }} | |
animate={{ scale: 1 }} | |
transition={{ | |
duration: 0.3, | |
}} | |
> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
width="28" | |
height="28" | |
> | |
<path | |
d="M 2 14 L 10 22 L 26 6" | |
fill="transparent" | |
strokeWidth="4" | |
stroke="currentColor" | |
strokeLinecap="round" | |
/> | |
</svg> | |
</motion.div> | |
) : ( | |
<form | |
style={{ | |
display: "grid", | |
gridTemplateRows, | |
gridTemplateColumns, | |
gap, | |
width: "100%", | |
height: "100%", | |
}} | |
onSubmit={handleSubmit} | |
method="POST" | |
> | |
{(withName || withEmail || withTwitterHandle) && ( | |
<div | |
style={{ | |
width: "100%", | |
display: "grid", | |
gridAutoFlow: | |
layout === "horizontal" | |
? "column" | |
: "row", | |
gap, | |
}} | |
> | |
{withName && ( | |
<input | |
className="framer-formspark-input" | |
type="text" | |
name="name" | |
placeholder={name.placeholder} | |
value={ | |
isCanvas ? name.value : nameValue | |
} | |
onChange={handleNameChange} | |
style={{ | |
...defaultStyle, | |
padding: paddingValue, | |
borderRadius, | |
fontFamily, | |
fontWeight, | |
fontSize, | |
background: inputs.fill, | |
color: inputs.color, | |
boxShadow: `inset 0 0 0 1px ${ | |
isNameError | |
? inputs.error | |
: "transparent" | |
}`, | |
}} | |
/> | |
)} | |
{withEmail && ( | |
<input | |
className="framer-formspark-input" | |
type="email" | |
name="email" | |
placeholder={email.placeholder} | |
value={ | |
isCanvas ? email.value : emailValue | |
} | |
onChange={handleEmailChange} | |
style={{ | |
...defaultStyle, | |
padding: paddingValue, | |
borderRadius, | |
fontFamily, | |
fontWeight, | |
fontSize, | |
background: inputs.fill, | |
color: inputs.color, | |
boxShadow: `inset 0 0 0 1px ${ | |
isEmailError | |
? inputs.error | |
: "transparent" | |
}`, | |
}} | |
/> | |
)} | |
</div> | |
)} | |
{withMessage && ( | |
<textarea | |
className="framer-formspark-input" | |
placeholder={message.placeholder} | |
name="message" | |
value={isCanvas ? message.value : messageValue} | |
onChange={handleMessageChange} | |
style={{ | |
...defaultStyle, | |
minHeight: 0, | |
padding: paddingValue, | |
resize: "vertical", | |
borderRadius, | |
background: inputs.fill, | |
fontFamily, | |
fontWeight, | |
fontSize, | |
color: inputs.color, | |
boxShadow: `inset 0 0 0 1px ${ | |
isMessageError | |
? inputs.error | |
: "transparent" | |
}`, | |
}} | |
/> | |
)} | |
{withTwitterHandle && ( | |
<input | |
className="framer-formspark-input" | |
type="text" | |
name="twitter-handle" | |
placeholder={twitterHandle.placeholder} | |
value={ | |
isCanvas | |
? twitterHandle.value | |
: twitterHandleValue | |
} | |
onChange={handleTwitterHandleChange} | |
style={{ | |
...defaultStyle, | |
minHeight: 0, | |
padding: paddingValue, | |
resize: "vertical", | |
borderRadius, | |
background: inputs.fill, | |
fontFamily, | |
fontWeight, | |
fontSize, | |
color: inputs.color, | |
boxShadow: `inset 0 0 0 1px ${ | |
isTwitterHandleError | |
? inputs.error | |
: "transparent" | |
}`, | |
}} | |
/> | |
)} | |
<div> | |
<motion.input | |
type="submit" | |
value={button.label} | |
style={{ | |
...defaultStyle, | |
borderRadius, | |
padding: paddingValue, | |
fontFamily, | |
fontWeight: button.fontWeight, | |
fontSize, | |
background: button.fill, | |
cursor: "pointer", | |
color: button.color, | |
zIndex: 1, | |
}} | |
transition={{ type: "ease", duration: 0.3 }} | |
whileHover={{ opacity: 0.8 }} | |
/> | |
{isLoading && ( | |
<div | |
style={{ | |
borderRadius, | |
position: "absolute", | |
display: "flex", | |
justifyContent: "center", | |
alignItems: "center", | |
width: "100%", | |
height: "100%", | |
left: 0, | |
top: 0, | |
zIndex: 2, | |
color: button.color, | |
background: button.fill, | |
}} | |
> | |
<motion.div | |
style={{ height: 16, width: 16 }} | |
initial={{ rotate: 0 }} | |
animate={{ rotate: 360 }} | |
transition={{ | |
duration: 2, | |
repeat: Infinity, | |
}} | |
> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
width="16" | |
height="16" | |
> | |
<path | |
d="M 8 0 C 3.582 0 0 3.582 0 8 C 0 12.419 3.582 16 8 16 C 12.418 16 16 12.419 16 8 C 15.999 3.582 12.418 0 8 0 Z M 8 14 C 4.687 14 2 11.314 2 8 C 2 4.687 4.687 2 8 2 C 11.314 2 14 4.687 14 8 C 14 11.314 11.314 14 8 14 Z" | |
fill="currentColor" | |
opacity="0.2" | |
/> | |
<path | |
d="M 8 0 C 12.418 0 15.999 3.582 16 8 C 16 8 16 9 15 9 C 14 9 14 8 14 8 C 14 4.687 11.314 2 8 2 C 4.687 2 2 4.687 2 8 C 2 8 2 9 1 9 C 0 9 0 8 0 8 C 0 3.582 3.582 0 8 0 Z" | |
fill="currentColor" | |
/> | |
</svg> | |
</motion.div> | |
</div> | |
)} | |
</div> | |
</form> | |
)} | |
</motion.div> | |
) | |
}, | |
[ | |
".framer-formspark-input::placeholder { color: var(--framer-formspark-placeholder-color) !important; }", | |
] | |
) | |
FormSparkWithTwitterHandle.defaultProps = { | |
fontSize: 16, | |
fontFamily: "Inter", | |
fontWeight: 400, | |
padding: 15, | |
paddingTop: 15, | |
paddingBottom: 15, | |
paddingLeft: 15, | |
paddingRight: 15, | |
borderRadius: 8, | |
topLeftRadius: 8, | |
topRightRadius: 8, | |
bottomRightRadius: 8, | |
bottomLeftRadius: 8, | |
gap: 15, | |
nameField: { value: undefined, placeholder: "Name" }, | |
email: { value: undefined, placeholder: "Email" }, | |
message: { value: undefined, placeholder: "Message" }, | |
inputs: { | |
fill: "#EBEBEB", | |
color: "#000", | |
placeholderColor: "rgba(0, 0, 0, 0.5)", | |
error: "#EE4444", | |
}, | |
layout: { | |
fill: "#EBEBEB", | |
color: "#000", | |
placeholderColor: "rgba(0, 0, 0, 0.5)", | |
error: "#EE4444", | |
}, | |
button: { label: "Sign Up", fontWeight: 600, fill: "#000", color: "#FFF" }, | |
} as any | |
addPropertyControls(FormSparkWithTwitterHandle, { | |
formId: { | |
title: "ID", | |
placeholder: "7PbPpGN3", | |
type: ControlType.String, | |
description: | |
"Create a [FormSpark](https://formspark.io/) account, add a new form and copy its ID. [Learn more…](https://www.framer.com/sites/integrations/formspark/)", | |
}, | |
withName: { | |
title: "Name", | |
type: ControlType.Boolean, | |
enabledTitle: "Show", | |
disabledTitle: "Hide", | |
defaultValue: true, | |
}, | |
nameField: { | |
title: " ", | |
type: ControlType.Object, | |
controls: { | |
placeholder: { | |
title: "Placeholder", | |
type: ControlType.String, | |
defaultValue: "Name", | |
}, | |
value: { | |
title: "Value", | |
type: ControlType.String, | |
defaultValue: "", | |
}, | |
}, | |
hidden: (props) => !props.withName, | |
}, | |
withEmail: { | |
title: "Email", | |
type: ControlType.Boolean, | |
enabledTitle: "Show", | |
disabledTitle: "Hide", | |
defaultValue: true, | |
}, | |
email: { | |
title: " ", | |
type: ControlType.Object, | |
controls: { | |
placeholder: { | |
title: "Placeholder", | |
type: ControlType.String, | |
defaultValue: "Email", | |
}, | |
value: { | |
title: "Value", | |
type: ControlType.String, | |
}, | |
}, | |
hidden: (props) => !props.withEmail, | |
}, | |
withMessage: { | |
title: "Message", | |
type: ControlType.Boolean, | |
enabledTitle: "Show", | |
disabledTitle: "Hide", | |
defaultValue: true, | |
}, | |
message: { | |
title: " ", | |
type: ControlType.Object, | |
controls: { | |
placeholder: { | |
title: "Placeholder", | |
type: ControlType.String, | |
defaultValue: "Message", | |
}, | |
value: { | |
title: "Value", | |
type: ControlType.String, | |
}, | |
}, | |
hidden: (props) => !props.withMessage, | |
}, | |
withTwitterHandle: { | |
title: "Twitter handle", | |
type: ControlType.Boolean, | |
enabledTitle: "Show", | |
disabledTitle: "Hide", | |
defaultValue: true, | |
}, | |
twitterHandle: { | |
title: "Twitter Handle", | |
type: ControlType.Object, | |
controls: { | |
placeholder: { | |
title: "Twitter handle", | |
type: ControlType.String, | |
defaultValue: "@framer", | |
}, | |
value: { | |
title: "Value", | |
type: ControlType.String, | |
}, | |
}, | |
hidden: (props) => !props.withTwitterHandle, | |
}, | |
layout: { | |
title: "Layout", | |
type: ControlType.Enum, | |
options: ["horizontal", "vertical"], | |
displaySegmentedControl: true, | |
defaultValue: "horizontal", | |
}, | |
inputs: { | |
title: "Inputs", | |
type: ControlType.Object, | |
controls: { | |
fill: { | |
title: "Fill", | |
type: ControlType.Color, | |
defaultValue: "#EBEBEB", | |
}, | |
color: { | |
title: "Text", | |
type: ControlType.Color, | |
defaultValue: "#000", | |
}, | |
placeholderColor: { | |
title: "Placeholder", | |
type: ControlType.Color, | |
defaultValue: "rgba(0, 0, 0, 0.5)", | |
}, | |
error: { | |
title: "Error", | |
type: ControlType.Color, | |
defaultValue: "#EE4444", | |
}, | |
}, | |
}, | |
button: { | |
title: "Button", | |
type: ControlType.Object, | |
controls: { | |
label: { | |
title: "Label", | |
type: ControlType.String, | |
defaultValue: "Sign Up", | |
}, | |
fontWeight: { | |
...fontControls.fontWeight, | |
defaultValue: 600, | |
}, | |
fill: { | |
title: "Fill", | |
type: ControlType.Color, | |
defaultValue: "#000", | |
}, | |
color: { | |
title: "Text", | |
type: ControlType.Color, | |
defaultValue: "#FFF", | |
}, | |
}, | |
}, | |
...fontControls, | |
fontSize: { | |
title: "Font Size", | |
type: ControlType.Number, | |
displayStepper: true, | |
defaultValue: 16, | |
}, | |
...paddingControl, | |
...borderRadiusControl, | |
gap: { | |
title: "Gap", | |
type: ControlType.Number, | |
displayStepper: true, | |
min: 0, | |
}, | |
onSubmit: { | |
type: ControlType.EventHandler, | |
}, | |
}) | |
const defaultStyle: CSSProperties = { | |
WebkitAppearance: "none", | |
display: "inline-block", | |
width: "100%", | |
lineHeight: "1.4em", | |
outline: "none", | |
border: "none", | |
} | |
export default FormSparkWithTwitterHandle |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment