From c6c6377e8bfae039620cd9b4cd5b4540fe92b7a1 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 12:30:54 +0100 Subject: [PATCH 1/8] Use `framer-motion` --- app/layouts/RootLayout.tsx | 5 ++++- package-lock.json | 33 +++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/app/layouts/RootLayout.tsx b/app/layouts/RootLayout.tsx index 462ad724ce..ae6c464b52 100644 --- a/app/layouts/RootLayout.tsx +++ b/app/layouts/RootLayout.tsx @@ -5,6 +5,7 @@ * * Copyright Oxide Computer Company */ +import { domAnimation, LazyMotion } from 'framer-motion' import { useEffect, useRef } from 'react' import { Outlet, useNavigation } from 'react-router-dom' @@ -26,7 +27,9 @@ export function RootLayout() { <> {process.env.MSW_BANNER ? : null} - + + + ) diff --git a/package-lock.json b/package-lock.json index 22942c9d2e..c9df585b5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "classnames": "^2.5.1", "date-fns": "^3.6.0", "filesize": "^10.1.1", + "framer-motion": "^11.1.7", "lodash.throttle": "^4.1.1", "match-sorter": "^6.3.4", "md5": "^2.3.0", @@ -10009,6 +10010,30 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.1.7.tgz", + "integrity": "sha512-cW11Pu53eDAXUEhv5hEiWuIXWhfkbV32PlgVISn7jRdcAiVrJ1S03YQQ0/DzoswGYYwKi4qYmHHjCzAH52eSdQ==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0", + "react-dom": "^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -26406,6 +26431,14 @@ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "dev": true }, + "framer-motion": { + "version": "11.1.7", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.1.7.tgz", + "integrity": "sha512-cW11Pu53eDAXUEhv5hEiWuIXWhfkbV32PlgVISn7jRdcAiVrJ1S03YQQ0/DzoswGYYwKi4qYmHHjCzAH52eSdQ==", + "requires": { + "tslib": "^2.4.0" + } + }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", diff --git a/package.json b/package.json index 54b1e9d2fe..0cb35cef79 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "classnames": "^2.5.1", "date-fns": "^3.6.0", "filesize": "^10.1.1", + "framer-motion": "^11.1.7", "lodash.throttle": "^4.1.1", "match-sorter": "^6.3.4", "md5": "^2.3.0", From 4494744c15abf971d51811c3ede1f8ed4212c406 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 12:31:25 +0100 Subject: [PATCH 2/8] Animate focus --- app/ui/lib/TextInput.tsx | 7 ++++--- app/ui/styles/index.css | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/app/ui/lib/TextInput.tsx b/app/ui/lib/TextInput.tsx index daeb5c2ada..472e88cb1a 100644 --- a/app/ui/lib/TextInput.tsx +++ b/app/ui/lib/TextInput.tsx @@ -74,15 +74,16 @@ export const TextInput = React.forwardRef< type={type} className={cn( `w-full rounded border-none px-3 - py-[0.6875rem] !outline-offset-1 text-sans-md - text-default bg-default placeholder:text-quaternary - focus:outline-none disabled:cursor-not-allowed disabled:text-tertiary disabled:bg-disabled`, + py-[0.6875rem] !outline-offset-1 text-sans-md text-default + bg-default placeholder:text-quaternary focus:outline-none disabled:cursor-not-allowed + disabled:text-tertiary disabled:bg-disabled`, error && 'focus-error', fieldClassName, disabled && 'text-disabled bg-disabled' )} aria-invalid={error} disabled={disabled} + data-1p-ignore {...fieldProps} /> diff --git a/app/ui/styles/index.css b/app/ui/styles/index.css index 3c15338f2a..4c867f77e6 100644 --- a/app/ui/styles/index.css +++ b/app/ui/styles/index.css @@ -116,6 +116,22 @@ input[type='number']:focus-visible { } } +a, +button, +.ox-tabs-panel, +[role='listbox'], +[role='option'], +[role='button'], +input[type='text'], +input[type='textarea'], +textarea[type='text'], +input[type='file'], +input[type='radio'], +input[type='checkbox'], +input[type='number'] { + @apply transition-[outline-width] duration-100 ease-out; +} + a:focus, button:focus, .ox-tabs-panel:focus, From 58266bf3112f30944ea1b1c62ea9b83e3a6c765a Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 12:31:43 +0100 Subject: [PATCH 3/8] Tweak dropdown easing --- app/ui/styles/components/menu-button.css | 2 +- app/ui/styles/index.css | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/app/ui/styles/components/menu-button.css b/app/ui/styles/components/menu-button.css index 87d3bb2dd9..824bb4eb5b 100644 --- a/app/ui/styles/components/menu-button.css +++ b/app/ui/styles/components/menu-button.css @@ -45,7 +45,7 @@ } .DropdownMenuContent { - animation: slide-down 0.2s ease; + animation: slide-down 0.2s var(--ease-out-quad); } @media (prefers-reduced-motion) { diff --git a/app/ui/styles/index.css b/app/ui/styles/index.css index 4c867f77e6..755de03739 100644 --- a/app/ui/styles/index.css +++ b/app/ui/styles/index.css @@ -44,6 +44,28 @@ :root { --content-gutter: 2.5rem; + + /* Nicer easing from: https://twitter.com/bdc */ + --ease-in-quad: cubic-bezier(0.55, 0.085, 0.68, 0.53); + --ease-in-cubic: cubic-bezier(0.55, 0.055, 0.675, 0.19); + --ease-in-quart: cubic-bezier(0.895, 0.03, 0.685, 0.22); + --ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06); + --ease-in-expo: cubic-bezier(0.95, 0.05, 0.795, 0.035); + --ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.335); + + --ease-out-quad: cubic-bezier(0.25, 0.46, 0.45, 0.94); + --ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1); + --ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1); + --ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); + --ease-out-expo: cubic-bezier(0.19, 1, 0.22, 1); + --ease-out-circ: cubic-bezier(0.075, 0.82, 0.165, 1); + + --ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955); + --ease-in-out-cubic: cubic-bezier(0.645, 0.045, 0.355, 1); + --ease-in-out-quart: cubic-bezier(0.77, 0, 0.175, 1); + --ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1); + --ease-in-out-expo: cubic-bezier(1, 0, 0, 1); + --ease-in-out-circ: cubic-bezier(0.785, 0.135, 0.15, 0.86); } @layer base { From 469bfae696f1bed0e9ce538f00e16ac3f048b6b4 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 12:32:16 +0100 Subject: [PATCH 4/8] Modal, sidemodal, button, clipboard animation tweaks --- app/ui/lib/Button.tsx | 34 ++++++++-- app/ui/lib/CopyToClipboard.tsx | 42 +++++++----- app/ui/lib/DialogOverlay.tsx | 11 ++- app/ui/lib/Modal.tsx | 86 ++++++++++-------------- app/ui/lib/SideModal.tsx | 119 +++++++++++++++------------------ 5 files changed, 150 insertions(+), 142 deletions(-) diff --git a/app/ui/lib/Button.tsx b/app/ui/lib/Button.tsx index ae972219b0..1b1328d1bc 100644 --- a/app/ui/lib/Button.tsx +++ b/app/ui/lib/Button.tsx @@ -6,6 +6,7 @@ * Copyright Oxide Computer Company */ import cn from 'classnames' +import { m } from 'framer-motion' import { forwardRef, type MouseEventHandler, type ReactNode } from 'react' import { Spinner } from '~/ui/lib/Spinner' @@ -90,9 +91,14 @@ export const Button = forwardRef( with={} > ) diff --git a/app/ui/lib/CopyToClipboard.tsx b/app/ui/lib/CopyToClipboard.tsx index 71a707de49..c096adec57 100644 --- a/app/ui/lib/CopyToClipboard.tsx +++ b/app/ui/lib/CopyToClipboard.tsx @@ -6,8 +6,8 @@ * Copyright Oxide Computer Company */ -import { animated, config, useTransition } from '@react-spring/web' import cn from 'classnames' +import { AnimatePresence, m } from 'framer-motion' import { useState } from 'react' import { Copy12Icon, Success12Icon } from '@oxide/design-system/icons/react' @@ -20,6 +20,11 @@ type Props = { className?: string } +const variants = { + hidden: { opacity: 0, scale: 0.75 }, + visible: { opacity: 1, scale: 1 }, +} + export const CopyToClipboard = ({ ariaLabel = 'Click to copy', text, @@ -35,14 +40,14 @@ export const CopyToClipboard = ({ }) } - const transitions = useTransition(hasCopied, { - from: { opacity: 0, transform: 'scale(0.8)' }, - enter: { opacity: 1, transform: 'scale(1)' }, - leave: { opacity: 0, transform: 'scale(0.8)' }, - config: config.stiff, - trail: 100, - initial: null, - }) + const animateProps = { + className: 'absolute inset-0 flex items-center justify-center', + variants, + initial: 'hidden', + animate: 'visible', + exit: 'hidden', + transition: { type: 'spring', duration: 0.2, bounce: 0 }, + } return ( ) } diff --git a/app/ui/lib/DialogOverlay.tsx b/app/ui/lib/DialogOverlay.tsx index 1d12dabcba..62786c9f33 100644 --- a/app/ui/lib/DialogOverlay.tsx +++ b/app/ui/lib/DialogOverlay.tsx @@ -6,8 +6,17 @@ * Copyright Oxide Computer Company */ +import { m } from 'framer-motion' import { forwardRef } from 'react' export const DialogOverlay = forwardRef((_, ref) => ( -
+ )) diff --git a/app/ui/lib/Modal.tsx b/app/ui/lib/Modal.tsx index a5cd7a477f..f43edb19ac 100644 --- a/app/ui/lib/Modal.tsx +++ b/app/ui/lib/Modal.tsx @@ -6,7 +6,7 @@ * Copyright Oxide Computer Company */ import * as Dialog from '@radix-ui/react-dialog' -import { animated, useTransition } from '@react-spring/web' +import { m } from 'framer-motion' import React, { createContext, forwardRef, useContext, useId } from 'react' import { Close12Icon } from '@oxide/design-system/icons/react' @@ -33,61 +33,43 @@ export type ModalProps = { export function Modal({ children, onDismiss, title, isOpen }: ModalProps) { const titleId = useId() - const AnimatedDialogContent = animated(Dialog.Content) - - const config = { tension: 650, mass: 0.125 } - - const transitions = useTransition(isOpen, { - from: { y: -5 }, - enter: { y: 0 }, - config: isOpen ? config : { duration: 0 }, - }) return ( - {transitions( - ({ y }, item) => - item && ( - { - if (!open) onDismiss() - }} - // https://github.com/radix-ui/primitives/issues/1159#issuecomment-1559813266 - modal={false} + { + if (!open) onDismiss() + }} + modal={false} + > + + + + + - - - `translate3d(-50%, ${-50 + value}%, 0px)`), - }} - // Prevents cancel loop on clicking on background over side - // modal to get out of image upload modal. Canceling out of - // confirm dialog returns focus to the dismissable layer, - // which triggers onDismiss again. And again. - // https://github.com/oxidecomputer/console/issues/1745 - onFocusOutside={(e) => e.preventDefault()} - > - {title && ( - - {title} - - )} - {children} - - - - - - - ) - )} + {title && ( + + {title} + + )} + {children} + + + + + + + ) } diff --git a/app/ui/lib/SideModal.tsx b/app/ui/lib/SideModal.tsx index f1404a4a4b..a5b113ae70 100644 --- a/app/ui/lib/SideModal.tsx +++ b/app/ui/lib/SideModal.tsx @@ -6,8 +6,8 @@ * Copyright Oxide Computer Company */ import * as Dialog from '@radix-ui/react-dialog' -import { animated, useTransition } from '@react-spring/web' import cn from 'classnames' +import { m } from 'framer-motion' import React, { createContext, useContext, useRef, type ReactNode } from 'react' import { Close12Icon, Error12Icon } from '@oxide/design-system/icons/react' @@ -53,79 +53,66 @@ export function SideModal({ onDismiss, title, subtitle, - isOpen, animate = true, errors, }: SideModalProps) { const titleId = 'side-modal-title' - const AnimatedDialogContent = animated(Dialog.Content) - - const config = { tension: 650, mass: 0.125 } - - const transitions = useTransition(isOpen, { - from: { x: 50 }, - enter: { x: 0 }, - config: isOpen && animate ? config : { duration: 0 }, - }) return ( - {transitions( - ({ x }, item) => - item && ( - { - if (!open) onDismiss() - }} - // https://github.com/radix-ui/primitives/issues/1159#issuecomment-1559813266 - modal={false} + { + if (!open) onDismiss() + }} + // https://github.com/radix-ui/primitives/issues/1159#issuecomment-1559813266 + modal={false} + > + + + + - - - `translate3d(${value}%, 0px, 0px)`), - }} - > - {title && ( - - <> - - - {errors && errors.length > 0 && ( -
- -
{errors.length} issues:
-
    - {errors.map((error, idx) => ( -
  • {error}
  • - ))} -
- - ) - } - title={errors.length > 1 ? 'Errors' : 'Error'} - /> -
- )} - -
- )} - {children} -
-
-
- ) - )} + {title && ( + + <> + + + {errors && errors.length > 0 && ( +
+ +
{errors.length} issues:
+
    + {errors.map((error, idx) => ( +
  • {error}
  • + ))} +
+ + ) + } + title={errors.length > 1 ? 'Errors' : 'Error'} + /> +
+ )} + +
+ )} + {children} + + + +
) } From 31d4d29cda83c0bbcc4063b5ac0b1a9003e71332 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 12:45:21 +0100 Subject: [PATCH 5/8] Update toast --- app/components/ToastStack.tsx | 47 +++++++++++++++-------------------- app/layouts/RootLayout.tsx | 2 +- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/app/components/ToastStack.tsx b/app/components/ToastStack.tsx index 56d5c9a4b5..5755a7d181 100644 --- a/app/components/ToastStack.tsx +++ b/app/components/ToastStack.tsx @@ -5,7 +5,7 @@ * * Copyright Oxide Computer Company */ -import { animated, useTransition } from '@react-spring/web' +import { AnimatePresence, m } from 'framer-motion' import { removeToast, useToastStore } from '~/stores/toast' import { Toast } from '~/ui/lib/Toast' @@ -13,34 +13,27 @@ import { Toast } from '~/ui/lib/Toast' export function ToastStack() { const toasts = useToastStore((state) => state.toasts) - const transition = useTransition(toasts, { - keys: (toast) => toast.id, - from: { opacity: 0, y: 10, scale: 95 }, - enter: { opacity: 1, y: 0, scale: 100 }, - leave: { opacity: 0, y: 10, scale: 95 }, - config: { duration: 100 }, - }) - return (
- {transition((style, item) => ( - `scale(${val}%, ${val}%)`), - }} - > - { - removeToast(item.id) - item.options.onClose?.() - }} - /> - - ))} + + {toasts.map((toast) => ( + + { + removeToast(toast.id) + toast.options.onClose?.() + }} + /> + + ))} +
) } diff --git a/app/layouts/RootLayout.tsx b/app/layouts/RootLayout.tsx index ae6c464b52..af2e862ec4 100644 --- a/app/layouts/RootLayout.tsx +++ b/app/layouts/RootLayout.tsx @@ -29,8 +29,8 @@ export function RootLayout() { {process.env.MSW_BANNER ? : null} + - ) } From 3923b18858a6514b93fde44d21a5a427d1aed5f2 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 13:08:08 +0100 Subject: [PATCH 6/8] Fix and disable timeout indicator --- app/hooks/index.ts | 1 - app/hooks/use-reduce-motion.tsx | 38 --------------------------------- app/main.tsx | 2 -- app/ui/lib/TimeoutIndicator.tsx | 28 +++++++++++++----------- app/ui/lib/Toast.tsx | 6 +++--- 5 files changed, 18 insertions(+), 57 deletions(-) delete mode 100644 app/hooks/use-reduce-motion.tsx diff --git a/app/hooks/index.ts b/app/hooks/index.ts index 3c2b3cb2bd..195ccab4cd 100644 --- a/app/hooks/index.ts +++ b/app/hooks/index.ts @@ -11,4 +11,3 @@ export * from './use-is-overflow' export * from './use-key' export * from './use-params' export * from './use-quick-actions' -export * from './use-reduce-motion' diff --git a/app/hooks/use-reduce-motion.tsx b/app/hooks/use-reduce-motion.tsx deleted file mode 100644 index e5db389868..0000000000 --- a/app/hooks/use-reduce-motion.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, you can obtain one at https://mozilla.org/MPL/2.0/. - * - * Copyright Oxide Computer Company - */ -import { Globals } from '@react-spring/web' -import { useEffect, useState } from 'react' - -Globals.assign({ skipAnimation: true }) - -const motionQuery = () => window.matchMedia('(prefers-reduced-motion: reduce)') - -/** - * Pulled from [react-reduce-motion](https://github.com/infiniteluke/react-reduce-motion). - */ -export function useReducedMotion() { - const [reducedMotion, setReducedMotion] = useState(motionQuery().matches) - useEffect(() => { - const mq = motionQuery() - const handleChange = () => setReducedMotion(mq.matches) - handleChange() - mq.addEventListener('change', handleChange) - return () => mq.removeEventListener('change', handleChange) - }, []) - return reducedMotion -} - -export function ReduceMotion() { - const prefersReducedMotion = useReducedMotion() - - useEffect(() => { - Globals.assign({ skipAnimation: prefersReducedMotion }) - }, [prefersReducedMotion]) - - return null -} diff --git a/app/main.tsx b/app/main.tsx index 20cde9eb73..cd32d19e88 100644 --- a/app/main.tsx +++ b/app/main.tsx @@ -15,7 +15,6 @@ import { queryClient } from '@oxide/api' import { ConfirmActionModal } from './components/ConfirmActionModal' import { ErrorBoundary } from './components/ErrorBoundary' -import { ReduceMotion } from './hooks' // stripped out by rollup in production import { startMockAPI } from './msw-mock-api' import { routes } from './routes' @@ -51,7 +50,6 @@ function render() { - {/* */} diff --git a/app/ui/lib/TimeoutIndicator.tsx b/app/ui/lib/TimeoutIndicator.tsx index ca19c20e2e..fb94663661 100644 --- a/app/ui/lib/TimeoutIndicator.tsx +++ b/app/ui/lib/TimeoutIndicator.tsx @@ -5,8 +5,8 @@ * * Copyright Oxide Computer Company */ -import { animated, Globals, useTransition } from '@react-spring/web' import cn from 'classnames' +import { m, useReducedMotion } from 'framer-motion' import { useTimeout } from './use-timeout' @@ -21,22 +21,24 @@ export const TimeoutIndicator = ({ onTimeoutEnd, className, }: TimeoutIndicatorProps) => { - const transitions = useTransition(true, { - from: { width: '0%' }, - enter: { width: '100%' }, - leave: { width: '100%' }, - config: { duration: timeout }, - }) + const shouldReduceMotion = useReducedMotion() useTimeout(onTimeoutEnd, timeout) - // Don't show progress bar if reduce motion is turned on - if (Globals.skipAnimation) return null + return null - return transitions((styles) => ( - - )) + ) } diff --git a/app/ui/lib/Toast.tsx b/app/ui/lib/Toast.tsx index 29b4251cca..7a9130f1e5 100644 --- a/app/ui/lib/Toast.tsx +++ b/app/ui/lib/Toast.tsx @@ -65,9 +65,9 @@ const secondaryTextColor: Record = { } const progressColor: Record = { - success: 'bg-accent-raise', - error: 'bg-destructive-raise', - info: 'bg-notice-raise', + success: 'bg-accent-hover', + error: 'bg-destructive-hover', + info: 'bg-notice-hover', } export const Toast = ({ From 4ff18dca1f624f387ff6cea38f3cd23a3715065e Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 13:08:20 +0100 Subject: [PATCH 7/8] Remove `react-spring` --- package-lock.json | 104 ---------------------------------------------- package.json | 1 - 2 files changed, 105 deletions(-) diff --git a/package-lock.json b/package-lock.json index c9df585b5a..48f7e965e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,6 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-tabs": "^1.0.3", "@react-aria/live-announcer": "^3.3.2", - "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.29.2", "@tanstack/react-query-devtools": "^5.29.2", "@tanstack/react-table": "^8.16.0", @@ -3715,66 +3714,6 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" } }, - "node_modules/@react-spring/animated": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", - "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", - "dependencies": { - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/core": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", - "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", - "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-spring/donate" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/shared": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", - "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", - "dependencies": { - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@react-spring/types": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", - "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" - }, - "node_modules/@react-spring/web": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", - "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", - "dependencies": { - "@react-spring/animated": "~9.7.3", - "@react-spring/core": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/@react-stately/calendar": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.4.4.tgz", @@ -22017,49 +21956,6 @@ "@swc/helpers": "^0.5.0" } }, - "@react-spring/animated": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.3.tgz", - "integrity": "sha512-5CWeNJt9pNgyvuSzQH+uy2pvTg8Y4/OisoscZIR8/ZNLIOI+CatFBhGZpDGTF/OzdNFsAoGk3wiUYTwoJ0YIvw==", - "requires": { - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - } - }, - "@react-spring/core": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.3.tgz", - "integrity": "sha512-IqFdPVf3ZOC1Cx7+M0cXf4odNLxDC+n7IN3MDcVCTIOSBfqEcBebSv+vlY5AhM0zw05PDbjKrNmBpzv/AqpjnQ==", - "requires": { - "@react-spring/animated": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - } - }, - "@react-spring/shared": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.3.tgz", - "integrity": "sha512-NEopD+9S5xYyQ0pGtioacLhL2luflh6HACSSDUZOwLHoxA5eku1UPuqcJqjwSD6luKjjLfiLOspxo43FUHKKSA==", - "requires": { - "@react-spring/types": "~9.7.3" - } - }, - "@react-spring/types": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.3.tgz", - "integrity": "sha512-Kpx/fQ/ZFX31OtlqVEFfgaD1ACzul4NksrvIgYfIFq9JpDHFwQkMVZ10tbo0FU/grje4rcL4EIrjekl3kYwgWw==" - }, - "@react-spring/web": { - "version": "9.7.3", - "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.3.tgz", - "integrity": "sha512-BXt6BpS9aJL/QdVqEIX9YoUy8CE6TJrU0mNCqSoxdXlIeNcEBWOfIyE6B14ENNsyQKS3wOWkiJfco0tCr/9tUg==", - "requires": { - "@react-spring/animated": "~9.7.3", - "@react-spring/core": "~9.7.3", - "@react-spring/shared": "~9.7.3", - "@react-spring/types": "~9.7.3" - } - }, "@react-stately/calendar": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/@react-stately/calendar/-/calendar-3.4.4.tgz", diff --git a/package.json b/package.json index 0cb35cef79..ed3be8c949 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-tabs": "^1.0.3", "@react-aria/live-announcer": "^3.3.2", - "@react-spring/web": "^9.7.3", "@tanstack/react-query": "^5.29.2", "@tanstack/react-query-devtools": "^5.29.2", "@tanstack/react-table": "^8.16.0", From 53218617659ce1f48858243dc3439f427f622be2 Mon Sep 17 00:00:00 2001 From: Benjamin Leonard Date: Wed, 1 May 2024 14:55:52 +0100 Subject: [PATCH 8/8] Oh maybe this? Handling `onFocusOutside` --- app/ui/lib/Modal.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/ui/lib/Modal.tsx b/app/ui/lib/Modal.tsx index f43edb19ac..1d71b6c019 100644 --- a/app/ui/lib/Modal.tsx +++ b/app/ui/lib/Modal.tsx @@ -46,7 +46,14 @@ export function Modal({ children, onDismiss, title, isOpen }: ModalProps) { - + e.preventDefault()} + >