Files
upstream-affine/packages/frontend/component/src/components/resize-panel/resize-panel.tsx
upstream-mirror 702cc698a7
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
2026-09-16 04:01:20 +05:00

248 lines
6.9 KiB
TypeScript

import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
import { forwardRef, useCallback, useLayoutEffect, useRef } from 'react';
import { useTransitionState } from 'react-transition-state';
import { useDropTarget } from '../../ui/dnd';
import { Tooltip, type TooltipProps } from '../../ui/tooltip';
import * as styles from './resize-panel.css';
export interface ResizeHandleProps extends React.HtmlHTMLAttributes<HTMLDivElement> {
resizing: boolean;
open: boolean;
minWidth: number;
maxWidth: number;
resizeHandlePos: 'left' | 'right';
resizeHandleOffset?: number;
resizeHandleVerticalPadding?: number;
onOpen: (open: boolean) => void;
onResizing: (resizing: boolean) => void;
onWidthChange: (width: number) => void;
onWidthChanged?: (width: number) => void;
tooltip?: TooltipProps['content'];
tooltipShortcut?: TooltipProps['shortcut'];
tooltipOptions?: Partial<Omit<TooltipProps, 'content' | 'shortcut'>>;
tooltipShortcutClassName?: string;
dropTargetOptions?: Parameters<typeof useDropTarget>[0];
}
export interface ResizePanelProps extends React.HtmlHTMLAttributes<HTMLDivElement> {
resizing: boolean;
open: boolean;
floating?: boolean;
minWidth: number;
maxWidth: number;
resizeHandlePos: 'left' | 'right';
resizeHandleOffset?: number;
resizeHandleVerticalPadding?: number;
resizeHandleTooltip?: TooltipProps['content'];
resizeHandleTooltipShortcut?: TooltipProps['shortcut'];
resizeHandleTooltipShortcutClassName?: string;
resizeHandleTooltipOptions?: Partial<
Omit<TooltipProps, 'content' | 'shortcut'>
>;
resizeHandleDropTargetOptions?: Parameters<typeof useDropTarget>[0];
enableAnimation?: boolean;
width: number;
unmountOnExit?: boolean;
onOpen: (open: boolean) => void;
onResizing: (resizing: boolean) => void;
onWidthChange: (width: number) => void;
onWidthChanged?: (width: number) => void;
}
const ResizeHandle = ({
className,
resizing,
minWidth,
maxWidth,
resizeHandlePos,
resizeHandleOffset,
resizeHandleVerticalPadding,
dropTargetOptions,
open,
onOpen,
onResizing,
onWidthChange,
onWidthChanged,
tooltip,
tooltipShortcut,
tooltipOptions,
tooltipShortcutClassName,
...rest
}: ResizeHandleProps) => {
const ref = useRef<HTMLDivElement>(null);
const onResizeStart = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
event.preventDefault();
let resized = false;
const panelContainer = ref.current?.parentElement;
if (!panelContainer) return;
// add cursor style to body
document.body.style.cursor = 'col-resize';
const { left: anchorLeft, right: anchorRight } =
panelContainer.getBoundingClientRect();
let lastWidth: number;
function onMouseMove(e: MouseEvent) {
e.preventDefault();
if (!panelContainer) return;
const newWidth = Math.min(
maxWidth,
Math.max(
resizeHandlePos === 'right'
? e.clientX - anchorLeft
: anchorRight - e.clientX,
minWidth
)
);
lastWidth = newWidth;
onWidthChange(newWidth);
onResizing(true);
resized = true;
}
document.addEventListener('mousemove', onMouseMove);
document.addEventListener(
'mouseup',
() => {
// if not resized, toggle sidebar
if (!resized) {
onOpen(false);
}
onResizing(false);
lastWidth && onWidthChanged?.(lastWidth);
document.removeEventListener('mousemove', onMouseMove);
document.body.style.cursor = '';
},
{ once: true }
);
},
[
maxWidth,
resizeHandlePos,
minWidth,
onWidthChange,
onResizing,
onWidthChanged,
onOpen,
]
);
const { dropTargetRef } = useDropTarget(dropTargetOptions, [
dropTargetOptions,
]);
return (
<Tooltip
content={tooltip}
shortcut={tooltipShortcut}
shortcutClassName={tooltipShortcutClassName}
{...tooltipOptions}
>
<div
{...rest}
data-testid="resize-handle"
ref={node => {
ref.current = node;
dropTargetRef.current = node;
}}
style={assignInlineVars({
[styles.resizeHandleOffsetVar]: `${resizeHandleOffset ?? 0}px`,
[styles.resizeHandleVerticalPadding]: `${
resizeHandleVerticalPadding ?? 0
}px`,
})}
className={clsx(styles.resizeHandleContainer, className)}
data-handle-position={resizeHandlePos}
data-resizing={resizing}
data-open={open}
onMouseDown={onResizeStart}
>
<div className={styles.resizerInner} />
</div>
</Tooltip>
);
};
const animationTimeout = 300;
export const ResizePanel = forwardRef<HTMLDivElement, ResizePanelProps>(
function ResizePanel(
{
children,
className,
resizing,
minWidth,
maxWidth,
width,
floating,
enableAnimation = true,
open,
unmountOnExit,
onOpen,
onResizing,
onWidthChange,
onWidthChanged,
resizeHandlePos,
resizeHandleOffset,
resizeHandleVerticalPadding,
resizeHandleTooltip,
resizeHandleTooltipShortcut,
resizeHandleTooltipShortcutClassName,
resizeHandleTooltipOptions,
resizeHandleDropTargetOptions,
...rest
},
ref
) {
const safeWidth = Math.min(maxWidth, Math.max(minWidth, width));
const [{ status }, toggle] = useTransitionState({
timeout: animationTimeout,
});
useLayoutEffect(() => {
toggle(open);
}, [open, toggle]);
return (
<div
{...rest}
ref={ref}
style={assignInlineVars({
[styles.panelWidthVar]: `${safeWidth}px`,
[styles.animationTimeout]: `${animationTimeout}ms`,
})}
className={clsx(className, styles.root)}
data-open={open}
data-transition-state={status}
data-is-floating={floating}
data-handle-position={resizeHandlePos}
data-enable-animation={enableAnimation && !resizing}
>
<div className={styles.content}>
{!(status === 'exited' && unmountOnExit !== false) && children}
</div>
<ResizeHandle
resizeHandlePos={resizeHandlePos}
resizeHandleOffset={resizeHandleOffset}
resizeHandleVerticalPadding={resizeHandleVerticalPadding}
tooltip={resizeHandleTooltip}
tooltipOptions={resizeHandleTooltipOptions}
tooltipShortcut={resizeHandleTooltipShortcut}
tooltipShortcutClassName={resizeHandleTooltipShortcutClassName}
maxWidth={maxWidth}
minWidth={minWidth}
onOpen={onOpen}
onResizing={onResizing}
onWidthChange={onWidthChange}
onWidthChanged={onWidthChanged}
open={open}
resizing={resizing}
dropTargetOptions={resizeHandleDropTargetOptions}
/>
</div>
);
}
);