mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 22:38:56 +08:00
feat(core): support draft filter (#12400)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added support for draft mode and completion callbacks across filter components, enabling stepwise filter creation and editing. - Enhanced filter menus and editors with external control via refs and new callback props for open/close state management. - Introduced new filter option group component for multi-step filter interactions. - Expanded tag filter methods for more granular filtering options. - Enabled controlled open state and close event handling for desktop and mobile menus. - Added programmatic control and completion callbacks to member selector and tags inline editors. - **Improvements** - Updated filter and tag editors with improved UI layouts and added "Done" buttons for easier completion. - Improved menu and editor accessibility by allowing programmatic open/close and completion event handling. - Refactored date filter components for modularity and consistent draft handling. - Separated draft filter state management in filter UI for clearer user interactions. - **Bug Fixes** - Refined date filter logic for more accurate "after" and "before" comparisons. - **Style** - Adjusted styles for draft filters and editor layouts to enhance visual clarity and user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import React, { useCallback, useImperativeHandle, useState } from 'react';
|
||||
|
||||
import type { MenuProps } from '../menu.types';
|
||||
import * as styles from '../styles.css';
|
||||
@@ -11,18 +11,52 @@ export const DesktopMenu = ({
|
||||
items,
|
||||
noPortal,
|
||||
portalOptions,
|
||||
rootOptions: { defaultOpen, modal, ...rootOptions } = {},
|
||||
rootOptions: {
|
||||
defaultOpen,
|
||||
modal,
|
||||
open,
|
||||
onOpenChange,
|
||||
onClose,
|
||||
...rootOptions
|
||||
} = {},
|
||||
contentOptions: {
|
||||
className = '',
|
||||
style: contentStyle = {},
|
||||
...otherContentOptions
|
||||
} = {},
|
||||
ref,
|
||||
}: MenuProps) => {
|
||||
const [innerOpen, setInnerOpen] = useState(defaultOpen);
|
||||
const finalOpen = open ?? innerOpen;
|
||||
|
||||
const handleOpenChange = useCallback(
|
||||
(open: boolean) => {
|
||||
setInnerOpen(open);
|
||||
onOpenChange?.(open);
|
||||
if (!open) {
|
||||
onClose?.();
|
||||
}
|
||||
},
|
||||
[onOpenChange, onClose]
|
||||
);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
changeOpen: (open: boolean) => {
|
||||
setInnerOpen(open);
|
||||
onOpenChange?.(open);
|
||||
},
|
||||
}),
|
||||
[onOpenChange]
|
||||
);
|
||||
|
||||
const ContentWrapper = noPortal ? React.Fragment : DropdownMenu.Portal;
|
||||
return (
|
||||
<DropdownMenu.Root
|
||||
defaultOpen={defaultOpen}
|
||||
modal={modal ?? false}
|
||||
open={finalOpen}
|
||||
onOpenChange={handleOpenChange}
|
||||
{...rootOptions}
|
||||
>
|
||||
<DropdownMenu.Trigger
|
||||
|
||||
@@ -8,15 +8,20 @@ import type {
|
||||
} from '@radix-ui/react-dropdown-menu';
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
|
||||
export interface MenuRef {
|
||||
changeOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export interface MenuProps {
|
||||
children: ReactNode;
|
||||
items: ReactNode;
|
||||
title?: string;
|
||||
portalOptions?: Omit<DropdownMenuPortalProps, 'children'>;
|
||||
rootOptions?: Omit<DropdownMenuProps, 'children'>;
|
||||
rootOptions?: Omit<DropdownMenuProps, 'children'> & { onClose?: () => void };
|
||||
contentOptions?: Omit<DropdownMenuContentProps, 'children'>;
|
||||
contentWrapperStyle?: CSSProperties;
|
||||
noPortal?: boolean;
|
||||
ref?: React.Ref<MenuRef>;
|
||||
}
|
||||
|
||||
export interface MenuItemProps
|
||||
|
||||
@@ -2,7 +2,13 @@ import { useI18n } from '@affine/i18n';
|
||||
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
|
||||
import { Slot } from '@radix-ui/react-slot';
|
||||
import clsx from 'clsx';
|
||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import { observeResize } from '../../../utils';
|
||||
import { Button } from '../../button';
|
||||
@@ -34,6 +40,7 @@ export const MobileMenu = ({
|
||||
} = {},
|
||||
contentWrapperStyle,
|
||||
rootOptions,
|
||||
ref,
|
||||
}: MenuProps) => {
|
||||
const [subMenus, setSubMenus] = useState<SubMenuContent[]>([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -82,10 +89,23 @@ export const MobileMenu = ({
|
||||
}
|
||||
setOpen(open);
|
||||
rootOptions?.onOpenChange?.(open);
|
||||
if (!open) {
|
||||
rootOptions?.onClose?.();
|
||||
}
|
||||
},
|
||||
[onInteractOutside, onPointerDownOutside, removeAllSubMenus, rootOptions]
|
||||
);
|
||||
|
||||
useImperativeHandle(
|
||||
ref,
|
||||
() => ({
|
||||
changeOpen: (open: boolean) => {
|
||||
onOpenChange(open);
|
||||
},
|
||||
}),
|
||||
[onOpenChange]
|
||||
);
|
||||
|
||||
const onItemClick = useCallback(
|
||||
(e: any) => {
|
||||
e.preventDefault();
|
||||
|
||||
Reference in New Issue
Block a user