Created
September 26, 2022 14:32
-
-
Save ricksmit3000/1198a80d6305b3ffd9d6e0c4458a25bb to your computer and use it in GitHub Desktop.
This file contains hidden or 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