diff --git a/packages/web/src/javascripts/Components/ChallengeModal/ChallengeModal.tsx b/packages/web/src/javascripts/Components/ChallengeModal/ChallengeModal.tsx index fd860e39c..decbf3a1c 100644 --- a/packages/web/src/javascripts/Components/ChallengeModal/ChallengeModal.tsx +++ b/packages/web/src/javascripts/Components/ChallengeModal/ChallengeModal.tsx @@ -239,18 +239,16 @@ const ChallengeModal: FunctionComponent = ({ application, mainApplication ref={setModalElement} close={cancelChallenge} hideOnInteractOutside={false} + backdropClassName={isFullScreenBlocker ? 'bg-passive-5' : ''} + className={classNames( + 'sn-component challenge-modal relative m-0 flex h-full w-full flex-col items-center rounded border-solid border-border bg-default p-0 md:h-auto md:!w-max', + !isMobileScreen && 'shadow-overlay-light', + isMobileOverlay && 'shadow-overlay-light border border-solid border-border', + )} > } customFooter={<>} disableCustomHeader={isMobileScreen} diff --git a/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx b/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx index 615498256..00b48c880 100644 --- a/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx +++ b/packages/web/src/javascripts/Components/ChangeEditor/ChangeEditorMenu.tsx @@ -253,7 +253,11 @@ const ChangeEditorMenu: FunctionComponent = ({ /> )} - + {note && pendingConversionItem && ( } disableCustomHeader={isMobileScreen} actions={actions} - className={{ - content: 'select-none md:min-w-[40vw]', - description: 'flex min-h-[50vh] flex-col', - }} + className="flex min-h-[50vh] flex-col" >
{ }, [application, toggle]) return ( - + ) diff --git a/packages/web/src/javascripts/Components/FilePreview/FilePreviewModal.tsx b/packages/web/src/javascripts/Components/FilePreview/FilePreviewModal.tsx index 6247ba32b..2ef75b50b 100644 --- a/packages/web/src/javascripts/Components/FilePreview/FilePreviewModal.tsx +++ b/packages/web/src/javascripts/Components/FilePreview/FilePreviewModal.tsx @@ -117,11 +117,6 @@ const FilePreviewModal = observer(({ application }: Props) => { = ({ application }) => { aria-label="File preview modal" isOpen={application.filePreviewModalController.isOpen} close={application.filePreviewModalController.dismiss} + className="md:!h-full md:max-h-[90%] md:!w-full md:max-w-[90%]" > diff --git a/packages/web/src/javascripts/Components/Modal/Modal.tsx b/packages/web/src/javascripts/Components/Modal/Modal.tsx index 76ad000cc..264c036ae 100644 --- a/packages/web/src/javascripts/Components/Modal/Modal.tsx +++ b/packages/web/src/javascripts/Components/Modal/Modal.tsx @@ -22,11 +22,7 @@ type Props = { title: string close: () => void actions?: ModalAction[] - className?: { - content?: string - description?: string - backdrop?: string - } + className?: string customHeader?: ReactNode disableCustomHeader?: boolean customFooter?: ReactNode @@ -37,7 +33,7 @@ const Modal = ({ title, close, actions = [], - className = {}, + className, customHeader, disableCustomHeader = false, customFooter, @@ -97,133 +93,121 @@ const Modal = ({ return ( <> -
-
- {customHeader && !disableCustomHeader ? ( - customHeader - ) : ( -
- - {leftSlotAction ? ( - - {leftSlotAction.label} - - ) : ( -
- )} -
- {extraActions.length > 0 && ( - <> - setShowAdvanced((show) => !show)} - slot="left" - ref={advancedOptionRef} - > -
- -
-
- setShowAdvanced((show) => !show)} - align="start" - portal={false} - className="!fixed divide-y divide-border border border-border" - > - {extraActions - .filter((action) => action.type !== 'cancel') - .map((action, index) => ( - - ))} - - - )} - {title} -
-
- -
- {rightSlotAction ? ( - - {rightSlotAction.label} - - ) : null} - -
- )} -
{children}
- {customFooter - ? customFooter - : sortedActions.length > 0 && ( -
+ + {leftSlotAction ? ( + - {sortedActions.map((action, index) => ( - - ))} -
+ {leftSlotAction.label} + + ) : ( +
)} -
+
+ {extraActions.length > 0 && ( + <> + setShowAdvanced((show) => !show)} + slot="left" + ref={advancedOptionRef} + > +
+ +
+
+ setShowAdvanced((show) => !show)} + align="start" + portal={false} + className="!fixed divide-y divide-border border border-border" + > + {extraActions + .filter((action) => action.type !== 'cancel') + .map((action, index) => ( + + ))} + + + )} + {title} +
+
+ +
+ {rightSlotAction ? ( + + {rightSlotAction.label} + + ) : null} +
+
+ )} +
{children}
+ {customFooter + ? customFooter + : sortedActions.length > 0 && ( +
+ {sortedActions.map((action, index) => ( + + ))} +
+ )} ) } diff --git a/packages/web/src/javascripts/Components/Modal/ModalOverlay.tsx b/packages/web/src/javascripts/Components/Modal/ModalOverlay.tsx index 7a9de55c5..7394a3ced 100644 --- a/packages/web/src/javascripts/Components/Modal/ModalOverlay.tsx +++ b/packages/web/src/javascripts/Components/Modal/ModalOverlay.tsx @@ -1,22 +1,42 @@ import { mergeRefs } from '@/Hooks/mergeRefs' import { Dialog, DialogOptions, useDialogStore } from '@ariakit/react' -import { ForwardedRef, forwardRef, ReactNode, useCallback } from 'react' +import { ForwardedRef, forwardRef, ReactNode, useCallback, useId } from 'react' import { useModalAnimation } from '../Modal/useModalAnimation' import { DialogWithClose } from '@/Utils/CloseOpenModalsAndPopovers' +import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery' +import { classNames } from '@standardnotes/snjs' type Props = { isOpen: boolean children: ReactNode + animate?: 'mobile' | 'desktop' | 'both' animationVariant?: 'horizontal' | 'vertical' close: () => void + className?: string + backdropClassName?: string } const ModalOverlay = forwardRef( ( - { isOpen, children, animationVariant, close, ...props }: Props & Partial, + { + isOpen, + children, + animationVariant, + close, + className, + backdropClassName, + animate, + ...props + }: Props & Partial, ref: ForwardedRef, ) => { - const [isMounted, setElement] = useModalAnimation(isOpen, animationVariant) + const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) + const [isMounted, setElement] = useModalAnimation( + isOpen, + isMobileScreen, + animationVariant, + (animate === 'mobile' && !isMobileScreen) || (animate === 'desktop' && isMobileScreen), + ) const dialog = useDialogStore({ open: isMounted, setOpen: (open) => { @@ -24,8 +44,23 @@ const ModalOverlay = forwardRef( close() } }, + animated: !isMobileScreen, }) + const portalId = useId() + const getPortalElement = useCallback(() => { + const id = 'portal/' + portalId + const existing = document.getElementById(id) + if (existing) { + return existing + } + const div = document.createElement('div') + div.id = id + div.className = 'fixed flex items-center justify-center left-0 top-0 z-modal h-full w-full pointer-events-none' + document.body.appendChild(div) + return div + }, [portalId]) + const addCloseMethod = useCallback( (element: HTMLDivElement | null) => { if (element) { @@ -42,11 +77,25 @@ const ModalOverlay = forwardRef( return ( + } ref={mergeRefs([setElement, addCloseMethod, ref])} store={dialog} modal={false} portal={true} + portalElement={getPortalElement} preventBodyScroll={true} hideOnInteractOutside={false} {...props} diff --git a/packages/web/src/javascripts/Components/Modal/useModalAnimation.ts b/packages/web/src/javascripts/Components/Modal/useModalAnimation.ts index 8f54d5276..35eb21805 100644 --- a/packages/web/src/javascripts/Components/Modal/useModalAnimation.ts +++ b/packages/web/src/javascripts/Components/Modal/useModalAnimation.ts @@ -1,5 +1,4 @@ import { useLifecycleAnimation } from '@/Hooks/useLifecycleAnimation' -import { useMediaQuery, MutuallyExclusiveMediaQueryBreakpoints } from '@/Hooks/useMediaQuery' export const IosModalAnimationEasing = 'cubic-bezier(.36,.66,.04,1)' @@ -52,40 +51,80 @@ const Animations = { transformOrigin: 'right', }, }, + nonMobile: { + enter: { + keyframes: [ + { + transform: 'scale(0.95)', + opacity: 0, + }, + { + transform: 'scale(1)', + opacity: 1, + }, + ], + transformOrigin: 'center', + }, + exit: { + keyframes: [ + { + transform: 'scale(1)', + opacity: 1, + }, + { + transform: 'scale(0.95)', + opacity: 0, + }, + ], + transformOrigin: 'center', + }, + }, } -export const useModalAnimation = (isOpen: boolean, variant: 'horizontal' | 'vertical' = 'vertical') => { - const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm) +const MobileOptions = { + easing: IosModalAnimationEasing, + duration: 250, + fill: 'forwards', +} +const NonMobileOptions = { + duration: 75, +} + +export const useModalAnimation = ( + isOpen: boolean, + isMobileScreen: boolean, + variant: 'horizontal' | 'vertical' = 'vertical', + disabled = false, +) => { return useLifecycleAnimation( { open: isOpen, enter: { - keyframes: Animations[variant].enter.keyframes, - options: { - easing: IosModalAnimationEasing, - duration: 250, - fill: 'forwards', - }, + keyframes: isMobileScreen ? Animations[variant].enter.keyframes : Animations.nonMobile.enter.keyframes, + options: isMobileScreen ? MobileOptions : NonMobileOptions, initialStyle: { - transformOrigin: Animations[variant].enter.transformOrigin, + transformOrigin: isMobileScreen + ? Animations[variant].enter.transformOrigin + : Animations.nonMobile.enter.transformOrigin, }, }, enterCallback: (element) => { + if (!isMobileScreen) { + return + } element.scrollTop = 0 }, exit: { - keyframes: Animations[variant].exit.keyframes, - options: { - easing: IosModalAnimationEasing, - duration: 250, - fill: 'forwards', - }, + keyframes: isMobileScreen ? Animations[variant].exit.keyframes : Animations.nonMobile.exit.keyframes, + options: isMobileScreen ? MobileOptions : NonMobileOptions, initialStyle: { - transformOrigin: Animations[variant].exit.transformOrigin, + transformOrigin: isMobileScreen + ? Animations[variant].exit.transformOrigin + : Animations.nonMobile.exit.transformOrigin, }, }, }, - !isMobileScreen, + disabled, ) } diff --git a/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx b/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx index 15ac59d92..82960feb2 100644 --- a/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx +++ b/packages/web/src/javascripts/Components/NoteView/NoteConflictResolutionModal/NoteConflictResolutionModal.tsx @@ -165,10 +165,7 @@ const NoteConflictResolutionModal = ({ return ( {
- + { return (