-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
23 changed files
with
1,736 additions
and
20 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
module.exports = { | ||
root: true, | ||
env: { browser: true, es2020: true }, | ||
extends: [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"plugin:react-hooks/recommended", | ||
], | ||
ignorePatterns: ["dist", ".eslintrc.cjs"], | ||
parser: "@typescript-eslint/parser", | ||
plugins: ["react-refresh"], | ||
rules: { | ||
"react-refresh/only-export-components": [ | ||
"warn", | ||
{ allowConstantExport: true }, | ||
], | ||
}, | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Logs | ||
logs | ||
*.log | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
node_modules | ||
dist | ||
dist-ssr | ||
*.local | ||
|
||
# Editor directories and files | ||
.vscode/* | ||
!.vscode/extensions.json | ||
.idea | ||
.DS_Store | ||
*.suo | ||
*.ntvs* | ||
*.njsproj | ||
*.sln | ||
*.sw? |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Farcaster Connect React |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Farcaster Connect</title> | ||
</head> | ||
<body> | ||
<div id="root"></div> | ||
<script type="module" src="/src/demo.tsx"></script> | ||
</body> | ||
</html> |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"name": "connect-react", | ||
"version": "0.0.0", | ||
"type": "module", | ||
"main": "./dist/connect-react.umd.cjs", | ||
"module": "./dist/connect-react.js", | ||
"exports": { | ||
".": { | ||
"import": "./dist/my-lib.js", | ||
"require": "./dist/my-lib.umd.cjs" | ||
} | ||
}, | ||
"scripts": { | ||
"dev": "vite", | ||
"build": "tsc && vite build", | ||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", | ||
"preview": "vite preview" | ||
}, | ||
"dependencies": { | ||
"@vanilla-extract/css": "^1.14.0", | ||
"qrcode": "^1.5.3", | ||
"react-remove-scroll": "^2.5.7" | ||
}, | ||
"devDependencies": { | ||
"@types/react": "^18.2.43", | ||
"@types/react-dom": "^18.2.17", | ||
"@typescript-eslint/eslint-plugin": "^6.14.0", | ||
"@typescript-eslint/parser": "^6.14.0", | ||
"@vanilla-extract/vite-plugin": "^3.9.3", | ||
"@vitejs/plugin-react": "^4.2.1", | ||
"eslint": "^8.55.0", | ||
"eslint-plugin-react-hooks": "^4.6.0", | ||
"eslint-plugin-react-refresh": "^0.4.5", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0", | ||
"typescript": "^5.2.2", | ||
"vite": "^4.4.11" | ||
}, | ||
"peerDependencies": { | ||
"react": ">= 17", | ||
"react-dom": "^18.2.0" | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { keyframes, style } from "@vanilla-extract/css"; | ||
|
||
const slideUp = keyframes({ | ||
"0%": { transform: "translateY(100%)" }, | ||
"100%": { transform: "translateY(0)" }, | ||
}); | ||
|
||
const fadeIn = keyframes({ | ||
"0%": { opacity: 0 }, | ||
"100%": { opacity: 1 }, | ||
}); | ||
|
||
const bleed = 200; | ||
|
||
export const overlay = style({ | ||
backdropFilter: "modalOverlay", | ||
background: "rgba(0, 0, 0, 0.3)", | ||
display: "flex", | ||
justifyContent: "center", | ||
position: "fixed", | ||
animation: `${fadeIn} 150ms ease`, | ||
bottom: -bleed, | ||
left: -bleed, | ||
padding: bleed, | ||
right: -bleed, | ||
top: -bleed, | ||
transform: "translateZ(0)", // This is required for content to render under the URL bar on iOS | ||
zIndex: 999999999, | ||
}); | ||
|
||
export const content = style({ | ||
display: "flex", | ||
flexDirection: "column", | ||
position: "relative", | ||
animation: `${slideUp} 350ms cubic-bezier(.15,1.15,0.6,1.00), ${fadeIn} 150ms ease`, | ||
maxWidth: "100vw", | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { | ||
MouseEventHandler, | ||
ReactNode, | ||
useCallback, | ||
useEffect, | ||
useState, | ||
} from "react"; | ||
import { createPortal } from "react-dom"; | ||
import { RemoveScroll } from "react-remove-scroll"; | ||
import * as styles from "./Dialog.css"; | ||
import { FocusTrap } from "./FocusTrap"; | ||
import { isMobile } from "../../utils"; | ||
|
||
const stopPropagation: MouseEventHandler<unknown> = (event) => | ||
event.stopPropagation(); | ||
|
||
interface DialogProps { | ||
open: boolean; | ||
onClose: () => void; | ||
titleId: string; | ||
onMountAutoFocus?: (event: Event) => void; | ||
children: ReactNode; | ||
} | ||
|
||
export function Dialog({ children, onClose, open, titleId }: DialogProps) { | ||
useEffect(() => { | ||
const handleEscape = (event: KeyboardEvent) => | ||
open && event.key === "Escape" && onClose(); | ||
|
||
document.addEventListener("keydown", handleEscape); | ||
|
||
return () => document.removeEventListener("keydown", handleEscape); | ||
}, [open, onClose]); | ||
|
||
const [bodyScrollable, setBodyScrollable] = useState(true); | ||
useEffect(() => { | ||
setBodyScrollable( | ||
getComputedStyle(window.document.body).overflow !== "hidden" | ||
); | ||
}, []); | ||
|
||
const handleBackdropClick = useCallback(() => onClose(), [onClose]); | ||
|
||
return ( | ||
<> | ||
{open | ||
? createPortal( | ||
<RemoveScroll enabled={bodyScrollable}> | ||
<div | ||
style={{ | ||
alignItems: isMobile() ? "flex-end" : "center", | ||
position: "fixed", | ||
}} | ||
aria-labelledby={titleId} | ||
aria-modal | ||
className={styles.overlay} | ||
onClick={handleBackdropClick} | ||
role="dialog" | ||
> | ||
<FocusTrap | ||
className={styles.content} | ||
onClick={stopPropagation} | ||
role="document" | ||
> | ||
{children} | ||
</FocusTrap> | ||
</div> | ||
</RemoveScroll>, | ||
document.body | ||
) | ||
: null} | ||
</> | ||
); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { useCallback, useEffect, useRef } from "react"; | ||
|
||
const moveFocusWithin = (element: HTMLElement, position: "start" | "end") => { | ||
const focusableElements = element.querySelectorAll( | ||
"button:not(:disabled), a[href]" | ||
) as NodeListOf<HTMLButtonElement | HTMLAnchorElement>; | ||
|
||
if (focusableElements.length === 0) return; | ||
|
||
focusableElements[ | ||
position === "end" ? focusableElements.length - 1 : 0 | ||
].focus(); | ||
}; | ||
|
||
export function FocusTrap(props: JSX.IntrinsicElements["div"]) { | ||
const contentRef = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
const previouslyActiveElement = document.activeElement; | ||
|
||
return () => { | ||
(previouslyActiveElement as HTMLElement).focus?.(); | ||
}; | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (contentRef.current) { | ||
const elementToFocus = | ||
contentRef.current.querySelector("[data-auto-focus]"); | ||
if (elementToFocus) { | ||
(elementToFocus as HTMLElement).focus(); | ||
} else { | ||
contentRef.current.focus(); | ||
} | ||
} | ||
}, []); | ||
|
||
return ( | ||
<> | ||
<div | ||
onFocus={useCallback( | ||
() => | ||
contentRef.current && moveFocusWithin(contentRef.current, "end"), | ||
[] | ||
)} | ||
// biome-ignore lint/a11y/noNoninteractiveTabindex: incorrect | ||
tabIndex={0} | ||
/> | ||
<div | ||
ref={contentRef} | ||
style={{ outline: "none" }} | ||
tabIndex={-1} | ||
{...props} | ||
/> | ||
<div | ||
onFocus={useCallback( | ||
() => | ||
contentRef.current && moveFocusWithin(contentRef.current, "start"), | ||
[] | ||
)} | ||
// biome-ignore lint/a11y/noNoninteractiveTabindex: incorrect | ||
tabIndex={0} | ||
/> | ||
</> | ||
); | ||
} |
10 changes: 10 additions & 0 deletions
10
packages/react/src/components/Provider/ConnectProvider.tsx
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ReactNode } from "react"; | ||
import { ModalProvider } from "./ModalProvider"; | ||
|
||
export interface ConnectProviderProps { | ||
children: ReactNode; | ||
} | ||
|
||
export function ConnectProvider({ children }: ConnectProviderProps) { | ||
return <ModalProvider>{children}</ModalProvider>; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { createContext, useContext } from "react"; | ||
|
||
interface ModalContextValue { | ||
isOpen: boolean; | ||
open: () => void; | ||
} | ||
|
||
export const ModalContext = createContext<ModalContextValue>({ | ||
isOpen: false, | ||
open: () => { | ||
/* no-op */ | ||
}, | ||
}); | ||
|
||
export const useModal = () => { | ||
return useContext(ModalContext); | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { ReactNode, useCallback, useMemo, useState } from "react"; | ||
import { SignInModal } from "../SignInDialog/SignInDialog"; | ||
import { ModalContext } from "./ModalContext"; | ||
|
||
interface ModalProviderProps { | ||
children: ReactNode; | ||
} | ||
|
||
export function ModalProvider({ children }: ModalProviderProps) { | ||
const [isOpen, setIsOpen] = useState(false); | ||
const open = useCallback(() => setIsOpen(true), [setIsOpen]); | ||
const close = useCallback(() => setIsOpen(false), [setIsOpen]); | ||
|
||
return ( | ||
<ModalContext.Provider | ||
value={useMemo( | ||
() => ({ | ||
isOpen, | ||
open, | ||
}), | ||
[isOpen, open] | ||
)} | ||
> | ||
{children} | ||
<SignInModal onClose={close} open={isOpen} /> | ||
</ModalContext.Provider> | ||
); | ||
} |
36 changes: 36 additions & 0 deletions
36
packages/react/src/components/SignInButton/SignInButton.tsx
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { useModal } from "../Provider/ModalContext.ts"; | ||
import { button } from "../styles.css.ts"; | ||
|
||
export function SignInButton() { | ||
const { open } = useModal(); | ||
|
||
const handleClick = () => { | ||
open(); | ||
}; | ||
|
||
return ( | ||
<button className={button} onClick={handleClick}> | ||
<svg | ||
width="20" | ||
height="20" | ||
viewBox="0 0 1000 1000" | ||
fill="none" | ||
xmlns="http://www.w3.org/2000/svg" | ||
> | ||
<path | ||
d="M257.778 155.556H742.222V844.445H671.111V528.889H670.414C662.554 441.677 589.258 373.333 500 373.333C410.742 373.333 337.446 441.677 329.586 528.889H328.889V844.445H257.778V155.556Z" | ||
fill="white" | ||
/> | ||
<path | ||
d="M128.889 253.333L157.778 351.111H182.222V746.667C169.949 746.667 160 756.616 160 768.889V795.556H155.556C143.283 795.556 133.333 805.505 133.333 817.778V844.445H382.222V817.778C382.222 805.505 372.273 795.556 360 795.556H355.556V768.889C355.556 756.616 345.606 746.667 333.333 746.667H306.667V253.333H128.889Z" | ||
fill="white" | ||
/> | ||
<path | ||
d="M675.556 746.667C663.282 746.667 653.333 756.616 653.333 768.889V795.556H648.889C636.616 795.556 626.667 805.505 626.667 817.778V844.445H875.556V817.778C875.556 805.505 865.606 795.556 853.333 795.556H848.889V768.889C848.889 756.616 838.94 746.667 826.667 746.667V351.111H851.111L880 253.333H702.222V746.667H675.556Z" | ||
fill="white" | ||
/> | ||
</svg> | ||
<span style={{ marginLeft: 9 }}>Sign in with Farcaster</span> | ||
</button> | ||
); | ||
} |
Oops, something went wrong.