React 파일 관리자를 만들어 봅시다 14장: 파일 관리자 본문
48363 단어 typescriptmongezjavascriptreact
작은 업데이트 진행 중 바 로더
진행률이
0
인 경우 흰색 배경만 표시합니다.// LoadingProgressBar.tsx
...
return (
<Progress
size="lg"
value={progress}
striped
// 👇🏻 We'll add the styles prop to style the root
styles={{
root: {
backgroundColor: progress === 0 ? "white" : undefined,
},
}}
label={progress > 0 ? `${progress}%` : undefined}
color={mapProgressColor()}
animate
/>
);
자체 구성 요소에서 오버레이 분리
이제 알다시피 오버레이는 파일 관리자 로딩 상태에 나타나므로 로딩이 발생할 때만 렌더링되는 별도의 구성 요소를 생성합니다.
새 파일
Content/ContentOverlay.tsx
을 만들고 다음 코드를 추가해 보겠습니다.// ContentOverlay.tsx
import { LoadingOverlay } from "@mantine/core";
import { useLoading } from "app/file-manager/hooks";
export default function ContentOverlay() {
const isLoading = useLoading();
return <LoadingOverlay visible={isLoading} overlayBlur={2} />;
}
이제
Content.tsx
로 가져오겠습니다.// Content.tsx
import { Card } from "@mantine/core";
import { ContentWrapper } from "./Content.styles";
import ContentOverlay from "./ContentOverlay"; 👈🏻
export default function Content() {
return (
<>
<Card shadow="sm">
<ContentWrapper>
<ContentOverlay /> 👈🏻
</ContentWrapper>
</Card>
</>
);
}
노드 목록
이제 큰 부분인 노드 목록에 대한 시간이므로 모든 노드를 렌더링할 새 파일
Content/NodesList.tsx
을 생성하겠습니다.그러나 우리는 노드를 두 개의 배열로 분할해야 합니다. 하나는 폴더용이고 다른 하나는 파일용입니다.
// NodesList.tsx
import { Grid } from "@mantine/core";
import { useKernel } from "app/file-manager/hooks";
import { useMemo } from "react";
import { DirectoryNode, FileNode } from "./ContentNode";
export default function NodesList() {
const kernel = useKernel();
const currentDirectoryNode = kernel.currentDirectoryNode;
const [directories, files] = useMemo(() => {
const node = currentDirectoryNode;
if (!node || !node.children?.length) return [[], []];
return [
node.children.filter(node => node.isDirectory),
node.children.filter(node => !node.isDirectory),
];
}, [currentDirectoryNode]);
return (
<>
<Grid>
{directories.map(node => (
<Grid.Col key={node.path} span={2}>
{node.name}
</Grid.Col>
))}
{files.map(node => (
<Grid.Col key={node.path} span={2}>
{node.name}
</Grid.Col>
))}
</Grid>
</>
);
}
우리는 모든 디렉터리 노드와 파일 노드를 나열하는 메모를 만들었지만 먼저 로드할 현재 디렉터리 노드가 있고 자식도 있는지 확인하려고 합니다.
이제 다음과 같은 내용이 표시됩니다.
디렉토리 노드 및 파일 노드
따라서 디렉터리 노드 목록 또는 파일 노드 목록에서 각 노드를 완전히 제어해야 하므로 두 구성 요소
DirectoryNode
및 FileNode
를 생성하여 각 노드를 처리하고 Content/ContentNode
에 생성됩니다. ) 디렉토리.// Content/ContentNode/DirectoryNode.tsx
import { DirectoryNodeProps } from "./ContentNode.types";
export default function DirectoryNode({ node }: DirectoryNodeProps) {
return <>{node.name}</>;
}
// Content/ContentNode/FileNode.tsx
import { FileNodeProps } from "./ContentNode.types";
export default function FileNode({ node }: FileNodeProps) {
return <>{node.name}</>;
}
두 구성 요소의 유형을 보유하도록
ContentNode.types.ts
를 생성해 보겠습니다.// Content/ContentNode/ContentNode.types.ts
import { Node } from "app/file-manager/Kernel";
export type FileNodeProps = {
node: Node;
};
export type DirectoryNodeProps = {
node: Node;
};
props에 추가한 모든 것은 노드 객체입니다.
이제 디렉터리의 모든 구성 요소를 내보내는 인덱스 파일을 만들어 보겠습니다.
// Content/ContentNode/index.ts
export { default as DirectoryNode } from "./DirectoryNode";
export { default as FileNode } from "./FileNode";
이제 NodesList에서 이 노드를 가져오겠습니다.
// NodesList.tsx
import { Grid } from "@mantine/core";
import { useKernel } from "app/file-manager/hooks";
import { useMemo } from "react";
👉🏻 import { DirectoryNode, FileNode } from "./ContentNode";
export default function NodesList() {
const kernel = useKernel();
const currentDirectoryNode = kernel.currentDirectoryNode;
const [directories, files] = useMemo(() => {
const node = currentDirectoryNode;
if (!node || !node.children?.length) return [[], []];
return [
node.children.filter(node => node.isDirectory),
node.children.filter(node => !node.isDirectory),
];
}, [currentDirectoryNode]);
return (
<>
<Grid>
{directories.map(node => (
<Grid.Col key={node.path} span={2}>
👉🏻 <DirectoryNode node={node} />
</Grid.Col>
))}
{files.map(node => (
<Grid.Col key={node.path} span={2}>
👉🏻 <FileNode node={node} />
</Grid.Col>
))}
</Grid>
</>
);
}
useCurrentDirectoryNode 후크
현재 디렉터리 노드를 반환하는 새 후크를 생성하고 변경 사항을 수신합니다. 그렇다면 이 후크를 사용하는 구성 요소를 다시 렌더링합니다.
// hooks/useCurrentDirectoryNode.ts
import { Node } from "app/file-manager/Kernel";
import { useEffect, useState } from "react";
import useKernel from "./useKernel";
export default function useCurrentDirectoryNode() {
const kernel = useKernel();
const [node, setNode] = useState<Node | undefined>(
kernel.currentDirectoryNode,
);
useEffect(() => {
const event = kernel.on("directoryChange", setNode);
return () => event.unsubscribe();
}, [kernel]);
return node;
}
우리는 단순히 커널에서 현재 디렉토리 노드를 가져와
state
에 설정한 다음 현재 노드 변경에 발생하는 모든 변경 사항을 관찰했습니다. 이 경우 상태를 업데이트하여 구성 요소가 다시 렌더링됩니다.useEvent 사용
이벤트
useEvent
를 처리할 이벤트unsubscribe
와 함께 사용할 수 있는 또 다른 좋은 후크가 있으므로 걱정할 필요가 없습니다.// hooks/useCurrentDirectoryNode.ts
👉🏻 import { useEvent } from "@mongez/react";
import { Node } from "app/file-manager/Kernel";
import { useState } from "react";
import useKernel from "./useKernel";
export default function useCurrentDirectoryNode() {
const kernel = useKernel();
const [node, setNode] = useState<Node | undefined>(
kernel.currentDirectoryNode,
);
👉🏻 useEvent(() => kernel.on("directoryChange", setNode));
return node;
}
이것은 이전 코드와 정확히 동일한 효과를 수행하지만 더 읽기 쉽고 구독 취소 이벤트에 대해 걱정할 필요가 없습니다.
Note that the
useEvent
callback must return the Event Subscription.
이제 현재 디렉터리 노드의 변경 사항을 수신하도록
NodesList
를 업데이트하여 새 렌더링을 만듭니다.// NodesList.tsx
import { Grid } from "@mantine/core";
// 👇🏻 import the hook
import { useCurrentDirectoryNode } from "app/file-manager/hooks";
import { useMemo } from "react";
import { DirectoryNode, FileNode } from "./ContentNode";
export default function NodesList() {
// 👇🏻 remove the useKernel hook and replace it with useCurrentDirectoryNode
❌ const kernel = useKernel();
❌ const currentDirectoryNode = kernel.currentDirectoryNode;
✅ const currentDirectoryNode = useCurrentDirectoryNode();
const [directories, files] = useMemo(() => {
const node = currentDirectoryNode;
if (!node || !node.children?.length) return [[], []];
return [
node.children.filter(node => node.isDirectory),
node.children.filter(node => !node.isDirectory),
];
}, [currentDirectoryNode]);
return (
<>
<Grid>
{directories.map(node => (
<Grid.Col key={node.path} span={2}>
<DirectoryNode node={node} />
</Grid.Col>
))}
{files.map(node => (
<Grid.Col key={node.path} span={2}>
<FileNode node={node} />
</Grid.Col>
))}
</Grid>
</>
);
}
DirectoryNode 및 FileNode의 UI 개선
이제 각 노드에 대한 멋진 UI를 만들어 보겠습니다. Mantine의
NavLink
구성 요소와 일부 아이콘도 사용할 것입니다.// DirectoryNode.tsx
import { NavLink, useMantineTheme } from "@mantine/core";
import { IconFolder } from "@tabler/icons";
import { useKernel } from "app/file-manager/hooks";
import { DirectoryNodeProps } from "./ContentNode.types";
export default function DirectoryNode({ node }: DirectoryNodeProps) {
// 👇🏻 get the theme
const theme = useMantineTheme();
const kernel = useKernel();
return (
// 👇🏻 use NavLink component
<NavLink
style={{
// 👇🏻 center the node and make the cursor to be default
textAlign: "center",
cursor: "default",
}}
// 👇🏻 add the icon, we'll use the IconFolder and give it some good styles from the theme
label={
<>
<IconFolder
fill={theme.colors.blue[4]}
strokeWidth={1.5}
color={theme.colors.blue[9]}
size={40}
/>
<div>{node.name}</div>
</>
}
/>
);
}
FileNode
구성 요소에서 동일한 작업을 수행해 보겠습니다.// FileNode.tsx
import { NavLink, useMantineTheme } from "@mantine/core";
import { IconFileInfo as Icon } from "@tabler/icons";
import { FileNodeProps } from "./ContentNode.types";
export default function FileNode({ node }: FileNodeProps) {
const theme = useMantineTheme();
return (
<NavLink
style={{
textAlign: "center",
cursor: "default",
}}
label={
<>
<Icon
fill={theme.colors.green[4]}
strokeWidth={1.5}
color={theme.colors.green[9]}
size={40}
/>
<div>{node.name}</div>
</>
}
/>
);
}
이제 최종 UI는 다음과 같습니다.
더블 클릭 시 디렉토리 열기
약간의 기능을 추가해 보겠습니다. 사용자가 디렉터리 노드를 두 번 클릭하면 열 수 있습니다.
// DirectoryNode.tsx
import { NavLink, useMantineTheme } from "@mantine/core";
import { IconFolder } from "@tabler/icons";
import { useKernel } from "app/file-manager/hooks";
import { DirectoryNodeProps } from "./ContentNode.types";
export default function DirectoryNode({ node }: DirectoryNodeProps) {
const theme = useMantineTheme();
const kernel = useKernel();
return (
<NavLink
style={{
textAlign: "center",
cursor: "default",
}}
// 👇🏻 add the onDoubleClick event
onDoubleClick={() => kernel.load(node.path)}
label={
<>
<IconFolder
fill={theme.colors.blue[4]}
strokeWidth={1.5}
color={theme.colors.blue[9]}
size={40}
/>
<div>{node.name}</div>
</>
}
/>
);
}
기사 저장소
Github Repository에서 챕터 파일을 볼 수 있습니다.
Don't forget the
main
branch has the latest updated code.
지금 어디 있는지 말해줘
이 시리즈를 저와 함께 후속 조치하는 경우 현재 위치와 어려움을 겪고 있는 부분을 알려주시면 최대한 도와드리겠습니다.
살람.
Reference
이 문제에 관하여(React 파일 관리자를 만들어 봅시다 14장: 파일 관리자 본문), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/hassanzohdy/lets-create-a-react-file-manager-chapter-xiv-file-manager-body-2bik텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)