diff --git a/packages/component/src/components/image-preview-modal/hooks/use-zoom.tsx b/packages/component/src/components/image-preview-modal/hooks/use-zoom.tsx index d51ffc6d81..78309e90f3 100644 --- a/packages/component/src/components/image-preview-modal/hooks/use-zoom.tsx +++ b/packages/component/src/components/image-preview-modal/hooks/use-zoom.tsx @@ -22,74 +22,6 @@ export const useZoomControls = ({ y: 0, }); - const zoomIn = useCallback(() => { - const image = imageRef.current; - - if (image && currentScale < 2) { - const newScale = currentScale + 0.1; - setCurrentScale(newScale); - image.style.width = `${image.naturalWidth * newScale}px`; - image.style.height = `${image.naturalHeight * newScale}px`; - } - }, [imageRef, currentScale]); - - const zoomOut = useCallback(() => { - const image = imageRef.current; - if (image && currentScale > 0.2) { - const newScale = currentScale - 0.1; - setCurrentScale(newScale); - image.style.width = `${image.naturalWidth * newScale}px`; - image.style.height = `${image.naturalHeight * newScale}px`; - const zoomedWidth = image.naturalWidth * newScale; - const zoomedHeight = image.naturalHeight * newScale; - const containerWidth = window.innerWidth; - const containerHeight = window.innerHeight; - if (zoomedWidth > containerWidth || zoomedHeight > containerHeight) { - image.style.transform = `translate(0px, 0px)`; - setImagePos({ x: 0, y: 0 }); - } - } - }, [imageRef, currentScale]); - - const checkZoomSize = useCallback(() => { - const { current: zoomArea } = zoomRef; - if (zoomArea) { - const image = zoomArea.querySelector('img'); - if (image) { - const zoomedWidth = image.naturalWidth * currentScale; - const zoomedHeight = image.naturalHeight * currentScale; - const containerWidth = window.innerWidth; - const containerHeight = window.innerHeight; - setIsZoomedBigger( - zoomedWidth > containerWidth || zoomedHeight > containerHeight - ); - } - } - }, [currentScale, zoomRef]); - - const resetZoom = useCallback(() => { - const image = imageRef.current; - if (image) { - const viewportWidth = window.innerWidth; - const viewportHeight = window.innerHeight; - const margin = 0.2; - - const availableWidth = viewportWidth * (1 - margin); - const availableHeight = viewportHeight * (1 - margin); - - const widthRatio = availableWidth / image.naturalWidth; - const heightRatio = availableHeight / image.naturalHeight; - - const newScale = Math.min(widthRatio, heightRatio); - setCurrentScale(newScale); - image.style.width = `${image.naturalWidth * newScale}px`; - image.style.height = `${image.naturalHeight * newScale}px`; - image.style.transform = 'translate(0px, 0px)'; - setImagePos({ x: 0, y: 0 }); - checkZoomSize(); - } - }, [checkZoomSize, imageRef]); - const handleDragStart = useCallback( (event: ReactMouseEvent) => { event?.preventDefault(); @@ -170,6 +102,86 @@ export const useZoomControls = ({ } }, [isDragging, dragEndImpl]); + const checkZoomSize = useCallback(() => { + const { current: zoomArea } = zoomRef; + if (zoomArea) { + const image = zoomArea.querySelector('img'); + if (image) { + const zoomedWidth = image.naturalWidth * currentScale; + const zoomedHeight = image.naturalHeight * currentScale; + const containerWidth = window.innerWidth; + const containerHeight = window.innerHeight; + setIsZoomedBigger( + zoomedWidth > containerWidth || zoomedHeight > containerHeight + ); + } + } + }, [currentScale, zoomRef]); + + const zoomIn = useCallback(() => { + const image = imageRef.current; + + if (image && currentScale < 2) { + const newScale = currentScale + 0.1; + setCurrentScale(newScale); + image.style.width = `${image.naturalWidth * newScale}px`; + image.style.height = `${image.naturalHeight * newScale}px`; + } + }, [imageRef, currentScale]); + + const zoomOut = useCallback(() => { + const image = imageRef.current; + if (image && currentScale > 0.2) { + const newScale = currentScale - 0.1; + setCurrentScale(newScale); + image.style.width = `${image.naturalWidth * newScale}px`; + image.style.height = `${image.naturalHeight * newScale}px`; + const zoomedWidth = image.naturalWidth * newScale; + const zoomedHeight = image.naturalHeight * newScale; + const containerWidth = window.innerWidth; + const containerHeight = window.innerHeight; + if (zoomedWidth > containerWidth || zoomedHeight > containerHeight) { + image.style.transform = `translate(0px, 0px)`; + setImagePos({ x: 0, y: 0 }); + } + } + }, [imageRef, currentScale]); + + const resetZoom = useCallback(() => { + const image = imageRef.current; + if (image) { + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + const margin = 0.2; + + const availableWidth = viewportWidth * (1 - margin); + const availableHeight = viewportHeight * (1 - margin); + + const widthRatio = availableWidth / image.naturalWidth; + const heightRatio = availableHeight / image.naturalHeight; + + const newScale = Math.min(widthRatio, heightRatio); + setCurrentScale(newScale); + image.style.width = `${image.naturalWidth * newScale}px`; + image.style.height = `${image.naturalHeight * newScale}px`; + image.style.transform = 'translate(0px, 0px)'; + setImagePos({ x: 0, y: 0 }); + checkZoomSize(); + } + }, [imageRef, checkZoomSize]); + + const resetScale = useCallback(() => { + const image = imageRef.current; + if (image) { + setCurrentScale(1); + image.style.width = `${image.naturalWidth}px`; + image.style.height = `${image.naturalHeight}px`; + image.style.transform = 'translate(0px, 0px)'; + setImagePos({ x: 0, y: 0 }); + } + }, [imageRef]); + + useEffect(() => { const handleScroll = (event: WheelEvent) => { const { deltaY } = event; @@ -201,6 +213,7 @@ export const useZoomControls = ({ zoomIn, zoomOut, resetZoom, + resetScale, isZoomedBigger, currentScale, handleDragStart, diff --git a/packages/component/src/components/image-preview-modal/index.css.ts b/packages/component/src/components/image-preview-modal/index.css.ts index 98bcbfcb75..e8c2712b12 100644 --- a/packages/component/src/components/image-preview-modal/index.css.ts +++ b/packages/component/src/components/image-preview-modal/index.css.ts @@ -1,18 +1,45 @@ import { baseTheme } from '@toeverything/theme'; -import { style } from '@vanilla-extract/css'; +import { keyframes, style } from '@vanilla-extract/css'; -export const imagePreviewModalStyle = style({ +const fadeInAnimation = keyframes({ + from: { opacity: 0 }, + to: { opacity: 1 }, +}); + +const fadeOutAnimation = keyframes({ + from: { opacity: 1 }, + to: { opacity: 0 }, +}); + +export const imagePreviewBackgroundStyle = style({ position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', zIndex: baseTheme.zIndexModal, + background: 'rgba(0, 0, 0, 0.75)', +}); + +export const imagePreviewModalStyle = style({ + height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', - // background: 'var(--affine-background-modal-color)', - background: 'rgba(0,0,0,0.75)', +}); + +export const loaded = style({ + opacity: 0, + animationName: fadeInAnimation, + animationDuration: '0.25s', + animationFillMode: 'forwards', +}); + +export const unloaded = style({ + opacity: 1, + animationName: fadeOutAnimation, + animationDuration: '0.25s', + animationFillMode: 'forwards', }); export const imagePreviewModalCloseButtonStyle = style({ @@ -33,6 +60,9 @@ export const imagePreviewModalCloseButtonStyle = style({ color: 'var(--affine-icon-color)', transition: 'background 0.2s ease-in-out', zIndex: 1, + marginTop: '38px', + marginRight: '38px', + }); export const imagePreviewModalGoStyle = style({ @@ -86,28 +116,48 @@ export const imagePreviewActionBarStyle = style({ display: 'flex', justifyContent: 'center', alignItems: 'center', - padding: '16px 0', backgroundColor: 'var(--affine-white)', borderRadius: '8px', boxShadow: '2px 2px 4px rgba(0, 0, 0, 0.3)', maxWidth: 'max-content', + minHeight: '44px', + maxHeight: '44px', }); export const groupStyle = style({ - display: 'inline-flex', + padding: '10px 0', + boxSizing: 'border-box', + display: 'flex', alignItems: 'center', justifyContent: 'center', - backgroundColor: 'var(--affine-white)', borderLeft: '1px solid #E3E2E4', }); export const buttonStyle = style({ - paddingLeft: '10px', - paddingRight: '10px', + minWidth: '24px', + height: '24px', + margin: '10px 6px', + padding: '0 0', + ':hover': { + backgroundColor: 'var(--affine-hover-color)', + backgroundSize: '24px 24px', + }, }); -export const scaleIndicatorStyle = style({ - margin: '0 8px', +export const buttonIconStyle = style({ + width: '20px', + height: '20px', +}); + +export const scaleIndicatorButtonStyle = style({ + minHeight: '100%', + maxWidth: 'max-content', + fontSize: '12px', + padding: '5px 5px', + + ':hover': { + backgroundColor: 'var(--affine-hover-color)', + }, }); export const imageBottomContainerStyle = style({ @@ -126,3 +176,8 @@ export const captionStyle = style({ padding: '10px', marginBottom: '21px', }); + +export const suspenseFallbackStyle = style({ + opacity: 0, + transition: 'opacity 2s ease-in-out', +}); diff --git a/packages/component/src/components/image-preview-modal/index.jotai.ts b/packages/component/src/components/image-preview-modal/index.jotai.ts index b1d113b281..326d6a4b76 100644 --- a/packages/component/src/components/image-preview-modal/index.jotai.ts +++ b/packages/component/src/components/image-preview-modal/index.jotai.ts @@ -2,6 +2,7 @@ import type { EmbedBlockDoubleClickData } from '@blocksuite/blocks'; import { atom } from 'jotai'; export const previewBlockIdAtom = atom(null); +export const hasAnimationPlayedAtom = atom(true); previewBlockIdAtom.onMount = set => { if (typeof window !== 'undefined') { diff --git a/packages/component/src/components/image-preview-modal/index.tsx b/packages/component/src/components/image-preview-modal/index.tsx index a16ade10fe..2c9511f54b 100644 --- a/packages/component/src/components/image-preview-modal/index.tsx +++ b/packages/component/src/components/image-preview-modal/index.tsx @@ -1,6 +1,7 @@ /// import '@blocksuite/blocks'; +import { Button, Tooltip } from '@affine/component'; import type { ImageBlockModel } from '@blocksuite/blocks'; import { assertExists } from '@blocksuite/global/utils'; import { @@ -21,24 +22,26 @@ import { Suspense, useCallback } from 'react'; import { useEffect, useRef, useState } from 'react'; import useSWR from 'swr'; -import Button from '../../ui/button/button'; import { useZoomControls } from './hooks/use-zoom'; import { + buttonIconStyle, buttonStyle, captionStyle, groupStyle, imageBottomContainerStyle, - imageNavigationControlStyle, imagePreviewActionBarStyle, + imagePreviewBackgroundStyle, imagePreviewModalCaptionStyle, imagePreviewModalCenterStyle, imagePreviewModalCloseButtonStyle, imagePreviewModalContainerStyle, - imagePreviewModalGoStyle, imagePreviewModalStyle, - scaleIndicatorStyle, + loaded, + scaleIndicatorButtonStyle, + unloaded, } from './index.css'; -import { previewBlockIdAtom } from './index.jotai'; +import { hasAnimationPlayedAtom, previewBlockIdAtom } from './index.jotai'; +import { toast } from './toast'; export type ImagePreviewModalProps = { workspace: Workspace; @@ -52,8 +55,189 @@ const ImagePreviewModalImpl = ( } ): ReactElement | null => { const [blockId, setBlockId] = useAtom(previewBlockIdAtom); + const zoomRef = useRef(null); + const imageRef = useRef(null); + const { + isZoomedBigger, + handleDrag, + handleDragStart, + handleDragEnd, + resetZoom, + zoomIn, + zoomOut, + resetScale, + currentScale, + } = useZoomControls({ zoomRef, imageRef }); + const [isOpen, setIsOpen] = useAtom(hasAnimationPlayedAtom); + const [hasPlayedAnimation, setHasPlayedAnimation] = useState(false); - const [bIsActionBarVisible, setBIsActionBarVisible] = useState(false); + useEffect(() => { + let timeoutId: NodeJS.Timeout; + + if (!isOpen) { + timeoutId = setTimeout(() => { + props.onClose(); + setIsOpen(true); + }, 300); + + return () => { + clearTimeout(timeoutId); + }; + } + + return () => {}; + }, [isOpen, props, setIsOpen]); + + const nextImageHandler = useCallback( + (blockId: string | null) => { + assertExists(blockId); + const workspace = props.workspace; + if (!hasPlayedAnimation) { + setHasPlayedAnimation(true); + } + const page = workspace.getPage(props.pageId); + assertExists(page); + const block = page.getBlockById(blockId); + assertExists(block); + const nextBlock = page + .getNextSiblings(block) + .find( + (block): block is ImageBlockModel => block.flavour === 'affine:embed' + ); + if (nextBlock) { + setBlockId(nextBlock.id); + } + }, + [props.pageId, props.workspace, setBlockId, hasPlayedAnimation] + ); + + const previousImageHandler = useCallback( + (blockId: string | null) => { + assertExists(blockId); + const workspace = props.workspace; + const page = workspace.getPage(props.pageId); + assertExists(page); + const block = page.getBlockById(blockId); + assertExists(block); + const prevBlock = page + .getPreviousSiblings(block) + .findLast( + (block): block is ImageBlockModel => block.flavour === 'affine:embed' + ); + if (prevBlock) { + setBlockId(prevBlock.id); + } + resetZoom(); + }, + [props.pageId, props.workspace, setBlockId, resetZoom] + ); + + const deleteHandler = useCallback( + (blockId: string) => { + const { pageId, workspace, onClose } = props; + + const page = workspace.getPage(pageId); + assertExists(page); + const block = page.getBlockById(blockId); + assertExists(block); + if ( + page + .getPreviousSiblings(block) + .findLast( + (block): block is ImageBlockModel => + block.flavour === 'affine:embed' + ) + ) { + const prevBlock = page + .getPreviousSiblings(block) + .findLast( + (block): block is ImageBlockModel => + block.flavour === 'affine:embed' + ); + if (prevBlock) { + setBlockId(prevBlock.id); + } + } else if ( + page + .getNextSiblings(block) + .find( + (block): block is ImageBlockModel => + block.flavour === 'affine:embed' + ) + ) { + const nextBlock = page + .getNextSiblings(block) + .find( + (block): block is ImageBlockModel => + block.flavour === 'affine:embed' + ); + if (nextBlock) { + setBlockId(nextBlock.id); + } + } else { + onClose(); + } + page.deleteBlock(block); + }, + [props, setBlockId] + ); + + const downloadHandler = useCallback( + async (blockId: string | null) => { + const workspace = props.workspace; + const page = workspace.getPage(props.pageId); + assertExists(page); + if (typeof blockId === 'string') { + const block = page.getBlockById(blockId) as ImageBlockModel; + assertExists(block); + const store = await block.page.blobs; + const url = store?.get(block.sourceId); + const img = await url; + if (!img) { + return; + } + const arrayBuffer = await img.arrayBuffer(); + const buffer = new Uint8Array(arrayBuffer); + let fileType: string; + if ( + buffer[0] === 0x47 && + buffer[1] === 0x49 && + buffer[2] === 0x46 && + buffer[3] === 0x38 + ) { + fileType = 'image/gif'; + } else if ( + buffer[0] === 0x89 && + buffer[1] === 0x50 && + buffer[2] === 0x4e && + buffer[3] === 0x47 + ) { + fileType = 'image/png'; + } else if ( + buffer[0] === 0xff && + buffer[1] === 0xd8 && + buffer[2] === 0xff && + buffer[3] === 0xe0 + ) { + fileType = 'image/jpeg'; + } else { + // unknown, fallback to png + console.error('unknown image type'); + fileType = 'image/png'; + } + const downloadUrl = URL.createObjectURL( + new Blob([arrayBuffer], { type: fileType }) + ); + const a = document.createElement('a'); + a.href = downloadUrl; + a.download = block.id ?? 'image'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + } + }, + [props.pageId, props.workspace] + ); const [caption, setCaption] = useState(() => { const page = props.workspace.getPage(props.pageId); assertExists(page); @@ -78,20 +262,10 @@ const ImagePreviewModalImpl = ( }, suspense: true, }); - const zoomRef = useRef(null); - const imageRef = useRef(null); - const { - zoomIn, - zoomOut, - isZoomedBigger, - handleDrag, - handleDragStart, - handleDragEnd, - resetZoom, - currentScale, - } = useZoomControls({ zoomRef, imageRef }); + const [prevData, setPrevData] = useState(() => data); const [url, setUrl] = useState(null); + if (prevData !== data) { if (url) { URL.revokeObjectURL(url); @@ -105,199 +279,15 @@ const ImagePreviewModalImpl = ( if (!url) { return null; } - - const nextImageHandler = (blockId: string | null) => { - assertExists(blockId); - const workspace = props.workspace; - - const page = workspace.getPage(props.pageId); - assertExists(page); - const block = page.getBlockById(blockId); - assertExists(block); - const nextBlock = page - .getNextSiblings(block) - .find( - (block): block is ImageBlockModel => block.flavour === 'affine:image' - ); - if (nextBlock) { - setBlockId(nextBlock.id); - } - }; - - const previousImageHandler = (blockId: string | null) => { - assertExists(blockId); - const workspace = props.workspace; - const page = workspace.getPage(props.pageId); - assertExists(page); - const block = page.getBlockById(blockId); - assertExists(block); - const prevBlock = page - .getPreviousSiblings(block) - .findLast( - (block): block is ImageBlockModel => block.flavour === 'affine:image' - ); - if (prevBlock) { - setBlockId(prevBlock.id); - } - }; - - const deleteHandler = (blockId: string) => { - const workspace = props.workspace; - - const page = workspace.getPage(props.pageId); - assertExists(page); - const block = page.getBlockById(blockId); - assertExists(block); - if ( - page - .getPreviousSiblings(block) - .findLast( - (block): block is ImageBlockModel => block.flavour === 'affine:image' - ) - ) { - const prevBlock = page - .getPreviousSiblings(block) - .findLast( - (block): block is ImageBlockModel => block.flavour === 'affine:image' - ); - if (prevBlock) { - setBlockId(prevBlock.id); - } - } else if ( - page - .getNextSiblings(block) - .find( - (block): block is ImageBlockModel => block.flavour === 'affine:image' - ) - ) { - const nextBlock = page - .getNextSiblings(block) - .find( - (block): block is ImageBlockModel => block.flavour === 'affine:image' - ); - if (nextBlock) { - const image = imageRef.current; - resetZoom(); - if (image) { - image.style.width = '100%'; // Reset the width to its original size - image.style.height = 'auto'; // Reset the height to maintain aspect ratio - } - setBlockId(nextBlock.id); - } - } else { - props.onClose(); - } - page.deleteBlock(block); - }; - - let actionbarTimeout: NodeJS.Timeout; - - const downloadHandler = async (blockId: string | null) => { - const workspace = props.workspace; - const page = workspace.getPage(props.pageId); - assertExists(page); - if (typeof blockId === 'string') { - const block = page.getBlockById(blockId) as ImageBlockModel; - assertExists(block); - const store = await block.page.blobs; - const url = store?.get(block.sourceId); - const img = await url; - if (!img) { - return; - } - const arrayBuffer = await img.arrayBuffer(); - const buffer = new Uint8Array(arrayBuffer); - let fileType: string; - if ( - buffer[0] === 0x47 && - buffer[1] === 0x49 && - buffer[2] === 0x46 && - buffer[3] === 0x38 - ) { - fileType = 'image/gif'; - } else if ( - buffer[0] === 0x89 && - buffer[1] === 0x50 && - buffer[2] === 0x4e && - buffer[3] === 0x47 - ) { - fileType = 'image/png'; - } else if ( - buffer[0] === 0xff && - buffer[1] === 0xd8 && - buffer[2] === 0xff && - buffer[3] === 0xe0 - ) { - fileType = 'image/jpeg'; - } else { - // unknown, fallback to png - console.error('unknown image type'); - fileType = 'image/png'; - } - const downloadUrl = URL.createObjectURL( - new Blob([arrayBuffer], { type: fileType }) - ); - const a = document.createElement('a'); - const event = new MouseEvent('click'); - a.download = block.id; - a.href = downloadUrl; - a.dispatchEvent(event); - - // cleanup - a.remove(); - URL.revokeObjectURL(downloadUrl); - } - }; - - const handleMouseEnter = () => { - clearTimeout(actionbarTimeout); - setBIsActionBarVisible(true); - }; - - const handleMouseLeave = () => { - actionbarTimeout = setTimeout(() => { - setBIsActionBarVisible(false); - }, 3000); - }; - return (
- event.target === event.currentTarget ? props.onClose() : null - } + onClick={event => { + if (event.target === event.currentTarget) { + setIsOpen(false); + } + }} > - {!isZoomedBigger ? ( -
- { - assertExists(blockId); - previousImageHandler(blockId); - }} - > - ❮ - - { - assertExists(blockId); - nextImageHandler(blockId); - }} - > - ❯ - -
- ) : ( - <> - )}
{caption} {isZoomedBigger ? null : ( @@ -323,144 +312,151 @@ const ImagePreviewModalImpl = (
- - {bIsActionBarVisible ? ( -
event.stopPropagation()} - > - {isZoomedBigger && caption !== '' ? ( -

{caption}

- ) : null} -
-
+ {isZoomedBigger && caption !== '' ? ( +

{caption}

+ ) : null} +
+
+
-
-
-
-
-
-
+
+
+ + + + +
- ) : null} +
); }; @@ -469,13 +465,16 @@ export const ImagePreviewModal = ( props: ImagePreviewModalProps ): ReactElement | null => { const [blockId, setBlockId] = useAtom(previewBlockIdAtom); + const [isOpen, setIsOpen] = useAtom(hasAnimationPlayedAtom); const handleKeyUp = useCallback( (event: KeyboardEvent) => { if (event.key === 'Escape') { event.preventDefault(); event.stopPropagation(); - setBlockId(null); + if (isOpen) { + setIsOpen(false); + } return; } @@ -495,7 +494,7 @@ export const ImagePreviewModal = ( .getPreviousSiblings(block) .findLast( (block): block is ImageBlockModel => - block.flavour === 'affine:image' + block.flavour === 'affine:embed' ); if (prevBlock) { setBlockId(prevBlock.id); @@ -505,7 +504,7 @@ export const ImagePreviewModal = ( .getNextSiblings(block) .find( (block): block is ImageBlockModel => - block.flavour === 'affine:image' + block.flavour === 'affine:embed' ); if (nextBlock) { setBlockId(nextBlock.id); @@ -516,7 +515,7 @@ export const ImagePreviewModal = ( event.preventDefault(); event.stopPropagation(); }, - [blockId, setBlockId, props.workspace, props.pageId] + [blockId, setBlockId, props.workspace, props.pageId, isOpen, setIsOpen] ); useEffect(() => { @@ -531,12 +530,38 @@ export const ImagePreviewModal = ( } return ( - }> - setBlockId(null)} - /> - +
+ }> + setBlockId(null)} + /> + + +
); }; diff --git a/packages/component/src/components/image-preview-modal/toast.ts b/packages/component/src/components/image-preview-modal/toast.ts new file mode 100644 index 0000000000..9ddcb1dd4b --- /dev/null +++ b/packages/component/src/components/image-preview-modal/toast.ts @@ -0,0 +1,21 @@ +import type { ToastOptions } from '@affine/component'; +import { toast as basicToast } from '@affine/component'; + +export const toast = (message: string, options?: ToastOptions) => { + const mainContainer = document.querySelector( + '[data-testid="image-preview-modal"]' + ) as HTMLElement; + return basicToast(message, { + portal: mainContainer || document.body, + ...options, + }); +}; + +declare global { + // global Events + interface WindowEventMap { + 'affine-toast:emit': CustomEvent<{ + message: string; + }>; + } +} diff --git a/packages/component/src/ui/toast/toast.ts b/packages/component/src/ui/toast/toast.ts index f7983d6bcc..7d21a113d0 100644 --- a/packages/component/src/ui/toast/toast.ts +++ b/packages/component/src/ui/toast/toast.ts @@ -67,7 +67,7 @@ export const toast = ( duration: 2500, } ) => { - if (!ToastContainer) { + if (!ToastContainer || (portal && !portal.contains(ToastContainer))) { ToastContainer = createToastContainer(portal); } @@ -126,6 +126,7 @@ export const toast = ( element.style.margin = '0'; element.style.padding = '0'; // wait for transition + // ToastContainer = null; element.addEventListener('transitionend', () => { element.remove(); }); diff --git a/packages/component/src/ui/tooltip/tooltip.tsx b/packages/component/src/ui/tooltip/tooltip.tsx index eb1ca9cf44..dfc11d4f6e 100644 --- a/packages/component/src/ui/tooltip/tooltip.tsx +++ b/packages/component/src/ui/tooltip/tooltip.tsx @@ -11,6 +11,8 @@ const StyledTooltip = styled(StyledPopperContainer)(() => { backgroundColor: 'var(--affine-tooltip)', color: 'var(--affine-white)', fontSize: 'var(--affine-font-sm)', + borderRadius: '8px', + marginBottom: '12px', }; });