diff --git a/docs/install.mdx b/docs/install.mdx index c78ff9d..745b270 100644 --- a/docs/install.mdx +++ b/docs/install.mdx @@ -3,9 +3,9 @@ sidebar_position: 1 description: 'Install `@react-native-google-signin/google-signin`. Covers paid (Universal Sign In) and free (Original) versions, requirements, and package manager setup.' --- -# Installation +import CreemEmbeddedCheckout from '@site/src/components/CreemEmbeddedCheckout'; -> We recommend [Expo](https://expo.dev) and [EAS](https://expo.dev/eas) for building and deploying your React Native app. Expo offers the best developer experience and is well-supported by this library. +# Installation The recommended option is [Universal Sign In](https://universal-sign-in.com) (paid). A free legacy version is also available — see [below](#public-version-free) for the differences. If you are an EAS customer, you may be able to access the paid version for free, [learn more](https://forms.gle/tpP7TfUGW1CwgaEZ8). @@ -13,6 +13,8 @@ Why paid? According to the [State of React Native Survey](https://results.2024.s [//]: # '🌟' + + ### Universal Sign In (premium) {/* #premium */} ⭐️ **Key Features**: @@ -166,3 +168,5 @@ The latest version of the Universal Sign In package supports (use older versions | ------------ | --------------- | | expo | 52.0.40 - 56 | | react-native | 0.76.0 - 0.86 | + +> We recommend [Expo](https://expo.dev) and [EAS](https://expo.dev/eas) for building and deploying your React Native app. Expo offers the best developer experience and is well-supported by this library. diff --git a/src/components/CreemEmbeddedCheckout/index.tsx b/src/components/CreemEmbeddedCheckout/index.tsx new file mode 100644 index 0000000..14ff934 --- /dev/null +++ b/src/components/CreemEmbeddedCheckout/index.tsx @@ -0,0 +1,191 @@ +import React, { useEffect, useId, useRef, useState } from 'react'; +import { createPortal } from 'react-dom'; +import './styles.css'; + +type CreemEmbeddedCheckoutProps = { + checkoutUrl: string; +}; + +const CREEM_ORIGIN = 'https://www.creem.io'; + +export default function CreemEmbeddedCheckout({ + checkoutUrl, +}: CreemEmbeddedCheckoutProps) { + const [isOpen, setIsOpen] = useState(false); + const [shouldLoadCheckout, setShouldLoadCheckout] = useState(false); + const titleId = useId(); + const closeButtonRef = useRef(null); + const iframeRef = useRef(null); + const modalContentRef = useRef(null); + + const loadCheckout = () => { + setShouldLoadCheckout(true); + }; + + const openCheckout = () => { + loadCheckout(); + setIsOpen(true); + }; + + useEffect(() => { + if (document.querySelector(`link[rel="preconnect"][href="${CREEM_ORIGIN}"]`)) { + return; + } + + const link = document.createElement('link'); + link.href = CREEM_ORIGIN; + link.rel = 'preconnect'; + link.crossOrigin = 'anonymous'; + document.head.appendChild(link); + }, []); + + useEffect(() => { + if (!isOpen) { + return; + } + + const previousOverflow = document.body.style.overflow; + const previousActiveElement = document.activeElement; + const getFocusableElements = () => + Array.from( + modalContentRef.current?.querySelectorAll( + 'button, iframe, [data-focus-sentinel]', + ) ?? [], + ).filter( + (element) => + !element.hasAttribute('disabled') && + element.getAttribute('aria-hidden') !== 'true', + ); + + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + setIsOpen(false); + return; + } + + if (event.key !== 'Tab') { + return; + } + + if ( + event.shiftKey && + document.activeElement === closeButtonRef.current + ) { + event.preventDefault(); + iframeRef.current?.focus(); + return; + } + + const focusableElements = getFocusableElements(); + const firstElement = focusableElements[0]; + const lastElement = focusableElements[focusableElements.length - 1]; + + if (!firstElement || !lastElement) { + event.preventDefault(); + return; + } + + if (event.shiftKey && document.activeElement === firstElement) { + event.preventDefault(); + lastElement.focus(); + return; + } + + if (!event.shiftKey && document.activeElement === lastElement) { + event.preventDefault(); + firstElement.focus(); + } + }; + + document.addEventListener('keydown', onKeyDown); + document.body.style.overflow = 'hidden'; + closeButtonRef.current?.focus(); + + return () => { + document.removeEventListener('keydown', onKeyDown); + document.body.style.overflow = previousOverflow; + + if (previousActiveElement instanceof HTMLElement) { + previousActiveElement.focus(); + } + }; + }, [isOpen]); + + return ( +
+
+
+

Universal Sign In license

+

Ready to install Universal Sign In?

+

+ Buy a license, then use the private npm registry setup below to add + the package to your app. +

+
+ +
+ {shouldLoadCheckout && + createPortal( +
+
+ iframeRef.current?.focus()} + /> +