Skip to content

Commit

Permalink
refactor(core): adjust modal animation (#7606)
Browse files Browse the repository at this point in the history
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/g3jz87HxbjOJpXV3FPT7/529d6c3f-4b23-43ac-84cc-171713d3dc72.mp4">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/g3jz87HxbjOJpXV3FPT7/529d6c3f-4b23-43ac-84cc-171713d3dc72.mp4">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/529d6c3f-4b23-43ac-84cc-171713d3dc72.mp4">CleanShot 2024-07-25 at 20.04.01.mp4</video>

When a modal is closed, sometimes its components are completely unmounted from the component tree, making it difficult to animate. This pr defining a custom element as the container of ReactDOM.portal, rewriting the `removeChild` function, and use `startViewTransition` when ReactDOM calls it to implement the animation.

# Save Input

Some inputs use blur event to save data, but when they are unmounted, blur event will not be triggered at all. This pr changes blur event to native addEventListener, which will be called after the DOM element is unmounted, so as to save data in time.
  • Loading branch information
EYHN committed Jul 26, 2024
1 parent 3eb09cd commit 6bc5337
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 198 deletions.
3 changes: 2 additions & 1 deletion .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"matchPackagePatterns": ["^@blocksuite"],
"excludePackageNames": ["@blocksuite/icons"],
"rangeStrategy": "replace",
"followTag": "canary"
"followTag": "canary",
"enabled": false
},
{
"groupName": "all non-major dependencies",
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
"@vanilla-extract/css": "^1.14.2",
"fake-indexeddb": "^6.0.0",
"storybook": "^7.6.17",
"storybook-dark-mode": "4.0.2",
"storybook-dark-mode": "4.0.1",
"typescript": "^5.4.5",
"vite": "^5.2.8",
"vitest": "1.6.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,12 @@ export const InlineEdit = ({
className={styles.inlineEditInput}
value={editingValue}
placeholder={placeholder}
onBlur={onBlur}
onEnter={onEnter}
onKeyDown={onKeyDown}
onChange={inputHandler}
style={inputWrapperInheritsStyles}
inputStyle={inputInheritsStyles}
onBlur={onBlur}
{...inputAttrs}
/>
}
Expand Down
25 changes: 21 additions & 4 deletions packages/frontend/component/src/ui/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ import clsx from 'clsx';
import type {
ChangeEvent,
CSSProperties,
FocusEventHandler,
ForwardedRef,
InputHTMLAttributes,
KeyboardEvent,
KeyboardEventHandler,
ReactNode,
} from 'react';
import { forwardRef, useCallback, useLayoutEffect, useRef } from 'react';
import {
forwardRef,
useCallback,
useEffect,
useLayoutEffect,
useRef,
} from 'react';

import { input, inputWrapper } from './style.css';

export type InputProps = {
disabled?: boolean;
onChange?: (value: string) => void;
onBlur?: FocusEventHandler<HTMLInputElement>;
onBlur?: (ev: FocusEvent & { currentTarget: HTMLInputElement }) => void;
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
autoSelect?: boolean;
noBorder?: boolean;
Expand All @@ -27,7 +32,7 @@ export type InputProps = {
type?: HTMLInputElement['type'];
inputStyle?: CSSProperties;
onEnter?: () => void;
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size'>;
} & Omit<InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'size' | 'onBlur'>;

export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
{
Expand All @@ -43,6 +48,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
endFix,
onEnter,
onKeyDown,
onBlur,
autoFocus,
autoSelect,
...otherProps
Expand All @@ -59,6 +65,17 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
}
}, [autoFocus, autoSelect, upstreamRef]);

// use native blur event to get event after unmount
// don't use useLayoutEffect here, because the cleanup function will be called before unmount
useEffect(() => {
if (!onBlur) return;
inputRef.current?.addEventListener('blur', onBlur as any);
return () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
inputRef.current?.removeEventListener('blur', onBlur as any);
};
}, [onBlur]);

return (
<div
className={clsx(inputWrapper, className, {
Expand Down
24 changes: 17 additions & 7 deletions packages/frontend/component/src/ui/menu/menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type {
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import clsx from 'clsx';
import type { ReactNode } from 'react';
import { useMemo } from 'react';

import * as styles from './styles.css';

Expand All @@ -16,13 +15,15 @@ export interface MenuProps {
portalOptions?: Omit<DropdownMenuPortalProps, 'children'>;
rootOptions?: Omit<DropdownMenuProps, 'children'>;
contentOptions?: Omit<DropdownMenuContentProps, 'children'>;
noPortal?: boolean;
}

export const Menu = ({
children,
items,
portalOptions,
rootOptions,
noPortal,
contentOptions: {
className = '',
style: contentStyle = {},
Expand All @@ -33,20 +34,29 @@ export const Menu = ({
<DropdownMenu.Root {...rootOptions}>
<DropdownMenu.Trigger asChild>{children}</DropdownMenu.Trigger>

<DropdownMenu.Portal {...portalOptions}>
{noPortal ? (
<DropdownMenu.Content
className={useMemo(
() => clsx(styles.menuContent, className),
[className]
)}
className={clsx(styles.menuContent, className)}
sideOffset={5}
align="start"
style={{ zIndex: 'var(--affine-z-index-popover)', ...contentStyle }}
{...otherContentOptions}
>
{items}
</DropdownMenu.Content>
</DropdownMenu.Portal>
) : (
<DropdownMenu.Portal {...portalOptions}>
<DropdownMenu.Content
className={clsx(styles.menuContent, className)}
sideOffset={5}
align="start"
style={{ zIndex: 'var(--affine-z-index-popover)', ...contentStyle }}
{...otherContentOptions}
>
{items}
</DropdownMenu.Content>
</DropdownMenu.Portal>
)}
</DropdownMenu.Root>
);
};
Loading

0 comments on commit 6bc5337

Please sign in to comment.