首页  >  文章  >  web前端  >  在 React 中创建平滑过渡的对话框组件(部分)

在 React 中创建平滑过渡的对话框组件(部分)

WBOY
WBOY原创
2024-07-18 01:23:391043浏览

Creating a Smooth Transitioning Dialog Component in React (Part )

第 1 部分:设置具有最小化/展开功能的基本对话框组件

欢迎来到我的关于在 React 中创建响应式对话框组件的四部分系列的第一部分。在本系列中,我将探索不同的方法来实现平滑的动画过渡,同时保持对话框的流体尺寸。在最初的部分中,我将设置具有最小化和展开功能的基本对话框组件。

请注意,可访问性和响应式设计不属于本系列考虑因素的一部分。主要重点是创建具有平滑动画过渡的可重用对话框组件。

本系列是我一直致力于的概念验证的一部分,旨在讨论和完善 UI 组件动画技术。我邀请其他开发人员提供反馈和见解来验证我的方法或提出改进建议。

设置基本对话框组件

让我们首先创建一个支持最小化和扩展的高度可重用的对话框组件。我将使用组合模式来确保对话框能够适应不断变化的内容。

文件结构:

src/
  components/
    FluidDialog/
      Dialog.js
      DialogContext.js
      DialogHeader.js
      DialogBody.js
      DialogFooter.js
      DialogContainer.js
      index.js
  App.js
  index.js

第 1 步:对话上下文

首先,我将创建一个上下文来管理对话框组件的状态。

要点:

  • DialogContext 将保存状态并提供在最小化和展开状态之间切换对话框的功能。
  • DialogProvider 组件初始化状态并通过上下文将其提供给对话框组件。
// src/components/FluidDialog/DialogContext.js
import { createContext, useContext, useId, useState } from 'react';

const DialogContext = createContext();

export function DialogProvider({
  rootRef,
  isExpandedByDefault,
  children,
  maxWidth,
}) {
  const dialogId = useId();
  const [isExpanded, setIsExpanded] = useState(isExpandedByDefault);

  return (
    <DialogContext.Provider
      value={{ dialogId, rootRef, isExpanded, setIsExpanded, maxWidth }}
    >
      {children}
    </DialogContext.Provider>
  );
}

export function useDialog() {
  return useContext(DialogContext);
}

第2步:对话框组件

接下来,我将创建使用上下文来处理扩展和最小化的主对话框组件。

要点:

  • Dialog 组件使用相关道具初始化上下文提供程序。
  • DialogComponent 样式组件处理对话框的基本样式和布局。
// src/components/FluidDialog/Dialog.js
import { useRef } from 'react';
import { styled } from 'styled-components';
import { DialogProvider } from './DialogContext';

export default function Dialog({
  id,
  isExpandedByDefault = true,
  maxWidth = 400,
  children,
}) {
  const rootRef = useRef(null);
  return (
    <DialogProvider
      dialogId={id}
      rootRef={rootRef}
      isExpandedByDefault={isExpandedByDefault}
    >
      <DialogComponent
        role="dialog"
        aria-labelledby={`${id}_label`}
        aria-describedby={`${id}_desc`}
        ref={rootRef}
        maxWidth={maxWidth}
      >
        {children}
      </DialogComponent>
    </DialogProvider>
  );
}

const DialogComponent = styled.section`
  max-width: ${({ maxWidth }) => (maxWidth ? `${maxWidth}px` : undefined)};
  position: absolute;
  right: 16px;
  bottom: 16px;
  border: 1px solid #ccc;
  border-radius: 6px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.35);
  overflow: hidden;
`;

第 3 步:附加组件

我将为对话框标题、正文、页脚和容器创建其他组件,以确保模块化和可重用性。

要点:

  • DialogHeader 包含一个按钮,可使用上下文在最小化和展开状态之间切换。
  • DialogContainer 包装正文和页脚内容,以便在 isExpanded 值更改时自动隐藏它们。
  • DialogBody 和DialogFooter 组件是对话框内容的简单容器。
// src/components/FluidDialog/DialogHeader.js
import { styled } from 'styled-components';
import { IconButton } from '../IconButton';
import { useDialog } from './DialogContext';

export default function DialogHeader({ children, expandedTitle }) {
  const { dialogId, isExpanded, setIsExpanded } = useDialog();

  return (
    <DialogHeaderComponent id={`${dialogId}_label`}>
      <ExpandedState isVisible={isExpanded}>
        <Title>{expandedTitle ?? children}</Title>
        <IconButtons>
          <IconButton
            icon="chevron-down"
            onClick={() => setIsExpanded(false)}
          />
        </IconButtons>
      </ExpandedState>
      <MinimizedState
        isVisible={!isExpanded}
        onClick={() => setIsExpanded(true)}
      >
        <Title>{children}</Title>
        <IconButtons>
          <IconButton icon="chevron-up" />
        </IconButtons>
      </MinimizedState>
    </DialogHeaderComponent>
  );
}

const DialogHeaderComponent = styled.div``;

const ExpandedState = styled.header`
  transition: opacity 0.3s;
  opacity: ${({ isVisible }) => (isVisible ? 1 : 0)};
  pointer-events: ${({ isVisible }) => (isVisible ? 'all' : 'none')};
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  background: #f3f3f3;
  display: flex;
  flex-direction: row;
`;

const MinimizedState = styled.header`
  transition: opacity 0.3s;
  opacity: ${({ isVisible }) => (isVisible ? 1 : 0)};
  pointer-events: ${({ isVisible }) => (isVisible ? 'all' : 'none')};
  background: #f3f3f3;
  display: flex;
  flex-direction: row;
  cursor: pointer;
`;

const Title = styled.span`
  flex-grow: 1;
  text-align: left;
  display: flex;
  align-items: center;
  padding: 0 16px;
`;

const IconButtons = styled.div``;
// src/components/FluidDialog/DialogContainer.js
import { styled } from 'styled-components';
import { useDialog } from './DialogContext';

export default function DialogContainer({ children }) {
  const { isExpanded } = useDialog();

  return (
    <DialogContainerComponent isVisible={isExpanded}>
      {children}
    </DialogContainerComponent>
  );
}

const DialogContainerComponent = styled.div`
  display: ${({ isVisible }) => (isVisible ? undefined : 'none')};
`;
// src/components/FluidDialog/DialogBody.js
import { styled } from 'styled-components';
import DialogContainer from './DialogContainer';
import { useDialog } from './DialogContext';

export default function DialogBody({ children }) {
  const { dialogId } = useDialog();

  return (
    <DialogBodyComponent>
      <DialogContainer>
        <DialogBodyContent id={`${dialogId}_desc`}>
          {children}
        </DialogBodyContent>
      </DialogContainer>
    </DialogBodyComponent>
  );
}

const DialogBodyComponent = styled.div``;

const DialogBodyContent = styled.div`
  padding: 8px 16px;
`;
// src/components/FluidDialog/DialogFooter.js
import { styled } from 'styled-components';
import DialogContainer from './DialogContainer';

export default function DialogFooter({ children }) {
  return (
    <DialogFooterComponent>
      <DialogContainer>
        <DialogFooterContent>{children}</DialogFooterContent>
      </DialogContainer>
    </DialogFooterComponent>
  );
}

const DialogFooterComponent = styled.div`
  background: #f3f3f3;
`;

const DialogFooterContent = styled.div`
  padding: 8px 16px;
`;

第四步:把它们放在一起

最后,我将在主应用程序中导入并使用对话框组件。

要点:

  • App 组件包括 Dialog 及其页眉、正文和页脚组件。
  • 此设置可确保对话框为后续部分中的进一步增强和动画做好准备。
// src/App.js
import React from 'react';
import Dialog from './components/FluidDialog/Dialog';
import DialogHeader from './components/FluidDialog/DialogHeader';
import DialogBody from './components/FluidDialog/DialogBody';
import DialogFooter from './components/FluidDialog/DialogFooter';

function App() {
  return (
    <div className="App">
      <Dialog>
        <DialogHeader>My dialog/DialogHeader>
        <DialogBody>This is the content of the dialog.</DialogBody>
        <DialogFooter>This is the footer of the dialog.</DialogFooter>
      </Dialog>
    </div>
  );
}

export default App;
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

您可以在 CodeSandbox 上访问完整的源代码。

您还可以查看实施的实时预览:

结论

在第一部分中,我在 React 中设置了一个基本对话框,具有最小化和展开功能。这个基础组件将作为后续文章中进一步增强的基础。对话框组件旨在拥抱其内容并适应变化,使其具有高度可重用性和灵活性。

请继续关注第 2 部分,我将深入研究向对话框过渡添加动画,探索不同的选项来实现平滑的效果。

我邀请其他开发人员提供反馈和意见,以帮助完善和改进这种方法。您的见解对于使概念验证更加稳健和有效非常宝贵。

以上是在 React 中创建平滑过渡的对话框组件(部分)的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn