diff --git a/assets/index.less b/assets/index.less index 4093e0f..59f57a3 100644 --- a/assets/index.less +++ b/assets/index.less @@ -4,6 +4,11 @@ @import './motion.less'; +// ::view-transition-old(root), /* 旧视图*/ +// ::view-transition-new(root) { /* 新视图*/ +// animation-duration: 0.6s; +// } + #arrow { .common() { width: 0; @@ -35,6 +40,20 @@ & > &-item { border-top: @borderStyle; + list-style-position: outside; + interpolate-size: allow-keywords; + overflow: hidden; + + &::details-content { + block-size: 0; + transition: block-size 0.6s, content-visibility 0.6s; + transition-behavior: allow-discrete; + } + + &[open]::details-content { + block-size: auto; + } + &:first-child { border-top: none; } diff --git a/docs/examples/_util/motionUtil.ts b/docs/examples/_util/motionUtil.ts index 7163efe..0d986c9 100644 --- a/docs/examples/_util/motionUtil.ts +++ b/docs/examples/_util/motionUtil.ts @@ -6,7 +6,7 @@ import type { const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 }); const getRealHeight: MotionEventHandler = (node) => ({ height: node.scrollHeight, opacity: 1 }); -const getCurrentHeight: MotionEventHandler = (node) => ({ height: node.offsetHeight }); +const getCurrentHeight: MotionEventHandler = (node) => ({ height: node?.offsetHeight ?? 0 }); const skipOpacityTransition: MotionEndEventHandler = (_, event) => (event as TransitionEvent).propertyName === 'height'; diff --git a/src/Collapse.tsx b/src/Collapse.tsx index 1b0b099..2229cab 100644 --- a/src/Collapse.tsx +++ b/src/Collapse.tsx @@ -46,18 +46,22 @@ const Collapse = React.forwardRef((props, ref) => }); const onItemClick = (key: React.Key) => - setActiveKey(() => { - if (accordion) { - return activeKey[0] === key ? [] : [key]; - } + // ? 用于解决react状态与details[open]状态不一致的问题 + // ? 具体参考issue https://github.com/facebook/react/issues/15486 + React.startTransition(() => { + setActiveKey(() => { + if (accordion) { + return activeKey[0] === key ? [] : [key]; + } - const index = activeKey.indexOf(key); - const isActive = index > -1; - if (isActive) { - return activeKey.filter((item) => item !== key); - } + const index = activeKey.indexOf(key); + const isActive = index > -1; + if (isActive) { + return activeKey.filter((item) => item !== key); + } - return [...activeKey, key]; + return [...activeKey, key]; + }); }); // ======================== Children ======================== diff --git a/src/Panel.tsx b/src/Panel.tsx index f5e7bb2..8894c5c 100644 --- a/src/Panel.tsx +++ b/src/Panel.tsx @@ -1,11 +1,11 @@ import classNames from 'classnames'; -import CSSMotion from '@rc-component/motion'; import KeyCode from '@rc-component/util/lib/KeyCode'; +import CSSMotion from '@rc-component/motion'; import React from 'react'; import type { CollapsePanelProps } from './interface'; import PanelContent from './PanelContent'; -const CollapsePanel = React.forwardRef((props, ref) => { +const CollapsePanel = React.forwardRef((props, ref) => { const { showArrow = true, headerClass, @@ -33,8 +33,10 @@ const CollapsePanel = React.forwardRef((prop const ifExtraExist = extra !== null && extra !== undefined && typeof extra !== 'boolean'; const collapsibleProps = { - onClick: () => { + onClick: (e: React.MouseEvent) => { onItemClick?.(panelKey); + e.preventDefault(); + e.stopPropagation(); }, onKeyDown: (e: React.KeyboardEvent) => { if (e.key === 'Enter' || e.keyCode === KeyCode.ENTER || e.which === KeyCode.ENTER) { @@ -79,7 +81,7 @@ const CollapsePanel = React.forwardRef((prop ); // ======================== HeaderProps ======================== - const headerProps: React.HTMLAttributes = { + const headerProps: React.HTMLAttributes = { className: headerClassName, style: styles?.header, ...(['header', 'icon'].includes(collapsible) ? {} : collapsibleProps), @@ -87,8 +89,8 @@ const CollapsePanel = React.forwardRef((prop // ======================== Render ======================== return ( -
-
+
+ {showArrow && iconNode} ((prop {header} {ifExtraExist &&
{extra}
} -
+ ((prop ); }} -
+ ); }); diff --git a/src/PanelContent.tsx b/src/PanelContent.tsx index f62bd30..9f08e53 100644 --- a/src/PanelContent.tsx +++ b/src/PanelContent.tsx @@ -18,13 +18,7 @@ const PanelContent = React.forwardRef< styles, } = props; - const [rendered, setRendered] = React.useState(isActive || forceRender); - - React.useEffect(() => { - if (forceRender || isActive) { - setRendered(true); - } - }, [forceRender, isActive]); + const rendered = isActive || forceRender; if (!rendered) { return null; diff --git a/src/interface.ts b/src/interface.ts index b420b7e..165c05d 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -16,7 +16,7 @@ export interface ItemType > { key?: CollapsePanelProps['panelKey']; label?: CollapsePanelProps['header']; - ref?: React.RefObject; + ref?: React.RefObject; } export interface CollapseProps { @@ -42,8 +42,7 @@ export interface CollapseProps { } export type SemanticName = 'header' | 'title' | 'body' | 'icon'; - -export interface CollapsePanelProps extends React.DOMAttributes { +export interface CollapsePanelProps extends React.DOMAttributes { id?: string; header?: React.ReactNode; prefixCls?: string; @@ -51,7 +50,7 @@ export interface CollapsePanelProps extends React.DOMAttributes showArrow?: boolean; className?: string; classNames?: Partial>; - style?: object; + style?: React.CSSProperties; styles?: Partial>; isActive?: boolean; openMotion?: CSSMotionProps; diff --git a/tests/__snapshots__/index.spec.tsx.snap b/tests/__snapshots__/index.spec.tsx.snap index 8e1a456..4742571 100644 --- a/tests/__snapshots__/index.spec.tsx.snap +++ b/tests/__snapshots__/index.spec.tsx.snap @@ -4,10 +4,10 @@ exports[`collapse props items should work with nested 1`] = `
-
- -
-
+ +
- -
-
-
+ +
- -
-
+ +
- -
+ + `; diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index f0d8117..568d221 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -85,9 +85,7 @@ describe('collapse', () => { expect(collapse.container.querySelectorAll('.rc-collapse-panel-active')).toHaveLength(1); fireEvent.click(header); jest.runAllTimers(); - expect(collapse.container.querySelector('.rc-collapse-panel-inactive')?.innerHTML).toBe( - '
second
', - ); + expect(collapse.container.querySelector('.rc-collapse-panel-inactive')).toBe(null); expect(collapse.container.querySelectorAll('.rc-collapse-panel-active').length).toBeFalsy(); }); @@ -433,9 +431,6 @@ describe('collapse', () => { jest.runAllTimers(); expect(container.querySelectorAll('.rc-collapse-panel-active')).toHaveLength(0); - expect(container.querySelector('.rc-collapse-panel')!.className).not.toContain( - 'rc-collapse-panel-active', - ); }); describe('wrapped in Fragment', () => { @@ -510,7 +505,6 @@ describe('collapse', () => { ); fireEvent.click(container.querySelector('.rc-collapse-header')!); expect(container.querySelectorAll('.rc-collapse-panel-active')).toHaveLength(0); - expect(container.querySelector('.rc-collapse-panel')).toHaveClass('rc-collapse-panel-inactive'); }); describe('prop: collapsible', () => {