diff --git a/packages/react-keyring/package.json b/packages/react-keyring/package.json index 047ff777..56f5bfaf 100644 --- a/packages/react-keyring/package.json +++ b/packages/react-keyring/package.json @@ -27,7 +27,8 @@ }, "homepage": "https://github.com/web3-storage/w3ui/tree/main/packages/react-keyring", "dependencies": { - "@w3ui/keyring-core": "workspace:^" + "@w3ui/keyring-core": "workspace:^", + "ariakit-react-utils": "0.17.0-next.27" }, "devDependencies": { "@ucanto/interface": "^4.0.3", diff --git a/packages/react-keyring/src/Authenticator.tsx b/packages/react-keyring/src/Authenticator.tsx index 3e346795..fb35c0e3 100644 --- a/packages/react-keyring/src/Authenticator.tsx +++ b/packages/react-keyring/src/Authenticator.tsx @@ -1,6 +1,8 @@ -import React, { - useState, createContext, useContext, useCallback, useMemo -} from 'react' +import type { As, Component, Props, Options } from 'ariakit-react-utils' +import type { ChangeEvent } from 'react' + +import React, { Fragment, useState, createContext, useContext, useCallback, useMemo, useEffect } from 'react' +import { createComponent, createElement } from 'ariakit-react-utils' import { useKeyring, KeyringContextState, KeyringContextActions } from './providers/Keyring' export type AuthenticatorContextState = KeyringContextState & { @@ -49,6 +51,16 @@ export const AuthenticatorContext = createContext([ } ]) +export const AgentLoader = ({ children }: { children: JSX.Element }): JSX.Element => { + const [, { loadAgent }] = useKeyring() + // eslint-disable-next-line + useEffect(() => { loadAgent() }, []) // load agent - once. + return children +} + +export type AuthenticatorRootOptions = Options +export type AuthenticatorRootProps = Props> + /** * Top level component of the headless Authenticator. * @@ -57,7 +69,7 @@ export const AuthenticatorContext = createContext([ * Designed to be used by Authenticator.Form, Authenticator.EmailInput * and others to make it easy to implement authentication UI. */ -export function Authenticator (props: any): JSX.Element { +export const AuthenticatorRoot: Component = createComponent((props) => { const [state, actions] = useKeyring() const { createSpace, registerSpace } = actions const [email, setEmail] = useState('') @@ -74,16 +86,23 @@ export function Authenticator (props: any): JSX.Element { } finally { setSubmitted(false) } - }, [setSubmitted, createSpace, registerSpace]) + }, [email, setSubmitted, createSpace, registerSpace]) const value = useMemo(() => [ { ...state, email, submitted, handleRegisterSubmit }, { ...actions, setEmail } ], [state, actions, email, submitted, handleRegisterSubmit]) return ( - + + + {createElement(Fragment, props)} + + ) -} +}) + +export type FormOptions = Options +export type FormProps = Props> /** * Form component for the headless Authenticator. @@ -91,12 +110,15 @@ export function Authenticator (props: any): JSX.Element { * A `form` designed to work with `Authenticator`. Any passed props will * be passed along to the `form` component. */ -Authenticator.Form = function Form (props: any) { +export const Form: Component = createComponent((props) => { const [{ handleRegisterSubmit }] = useAuthenticator() return ( -
+ createElement('form', { ...props, onSubmit: handleRegisterSubmit }) ) -} +}) + +export type EmailInputOptions = Options +export type EmailInputProps = Props> /** * Input component for the headless Uploader. @@ -104,12 +126,14 @@ Authenticator.Form = function Form (props: any) { * An email `input` designed to work with `Authenticator.Form`. Any passed props will * be passed along to the `input` component. */ -Authenticator.EmailInput = function EmailInput (props: any) { +export const EmailInput: Component = createComponent(props => { const [{ email }, { setEmail }] = useAuthenticator() - return ( - setEmail(e.target.value)} /> - ) -} + const onChange = useCallback((e: ChangeEvent) => setEmail(e.target.value), []) + return createElement('input', { ...props, type: 'email', value: email, onChange }) +}) + +export type CancelButtonOptions = Options +export type CancelButtonProps = Props> /** * A button that will cancel space registration. @@ -117,12 +141,10 @@ Authenticator.EmailInput = function EmailInput (props: any) { * A `button` designed to work with `Authenticator.Form`. Any passed props will * be passed along to the `button` component. */ -Authenticator.CancelButton = function CancelButton (props: any) { +export const CancelButton: Component = createComponent((props) => { const [, { cancelRegisterSpace }] = useAuthenticator() - return ( -