首頁  >  文章  >  web前端  >  在 React 中建立平滑過渡的對話方塊元件(部分)

在 React 中建立平滑過渡的對話方塊元件(部分)

WBOY
WBOY原創
2024-07-18 01:23:391044瀏覽

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
上一篇:前端技術下一篇:前端技術