React File Manager를 만들어 봅시다. 20장: Create Directory 버튼 만들기

이제 우리는 지금까지 도구 모음과 작업에 만족하므로 create directory 버튼과 작업을 만들어 보겠습니다.

UI



UI는 매우 간단합니다. 더하기 아이콘이 있는 버튼이 있고 사용자가 버튼을 클릭하면 새 디렉토리를 생성하는 양식이 있는 모달이 표시됩니다.

// components/Toolbar/Buttons/CreateDirectoryButton.tsx
export default function CreateDirectoryButton() {
  return <div>CreateDirectoryButton</div>;
}


이제 툴바에서 가져오겠습니다.

// components/Toolbar/Toolbar.tsx
import { Grid } from "@mantine/core";
import CreateDirectoryButton from "./Buttons/CreateDirectoryButton";
import HomeDirectoryButton from "./Buttons/HomeDirectoryButton";
import { ToolbarWrapper } from "./Toolbar.styles";

const buttons = [
  HomeDirectoryButton,
  CreateDirectoryButton,
];

export default function Toolbar() {
  return (
    <>
      <ToolbarWrapper shadow="sm">
        <Grid>
          {buttons.map((Button, index) => (
            <Button key={index} />
          ))}
        </Grid>
      </ToolbarWrapper>
    </>
  );
}


이제 다음과 같이 보입니다.



이제 기능을 향상시켜 실제로 HomeDirectoryButton를 복사/붙여넣고 필요에 따라 변경합니다.

// components/Toolbar/Buttons/CreateDirectoryButton.tsximport { Text, ThemeIcon, Tooltip, useMantineTheme } from "@mantine/core";
import { IconCirclePlus } from "@tabler/icons";
import {
  ToolbarButtonText,
  ToolBarButtonWrapper,
  ToolbarIcon,
} from "../Toolbar.styles";

export default function CreateDirectoryButton() {
  const theme = useMantineTheme();

  return (
    <Tooltip
      label={"Create New Directory"}
      position="bottom"
      transition="slide-up">
      <ToolBarButtonWrapper>
        <ToolbarIcon variant="subtle">
          <ThemeIcon
            variant="gradient"
            gradient={{ from: "indigo", to: "cyan" }}>
            <IconCirclePlus size={18} color={theme.white} />
          </ThemeIcon>
        </ToolbarIcon>
        <ToolbarButtonText>
          <Text color="blue">Directory</Text>
        </ToolbarButtonText>
      </ToolBarButtonWrapper>
    </Tooltip>
  );
}


나는 그것이 최고의 모습은 아니지만 여전히 그렇게 나쁘지는 않다는 것을 압니다 😁

모달 팝업



이제 우리는 디렉토리를 생성하기 위한 폼을 표시하는 팝업을 생성하고 싶으므로 이를 위한 새 구성 요소를 생성해 보겠습니다.

// components/Toolbar/Modals/CreateDirectoryModal.tsx
import { Modal } from "@mantine/core";

export type CreateDirectoryModalProps = {
  open: boolean;
  onClose: () => void;
};

export default function CreateDirectoryModal({
  open,
  onClose,
}: CreateDirectoryModalProps) {
  return (
    <Modal opened={open} onClose={onClose}>
      <div>Modal content</div>
    </Modal>
  );
}


이제 버튼 구성 요소에서 열기/닫기 상태를 만들고 모달을 가져옵니다.

// components/Toolbar/Buttons/CreateDirectoryButton.tsx
import { Text, ThemeIcon, Tooltip, useMantineTheme } from "@mantine/core";
import { IconCirclePlus } from "@tabler/icons";
import { useState } from "react";
import {
  ToolbarButtonText,
  ToolBarButtonWrapper,
  ToolbarIcon,
} from "../Toolbar.styles";
import CreateDirectoryModal from "./CreateDirectoryModal";

export default function CreateDirectoryButton() {
  const theme = useMantineTheme();

  const [openModal, setOpenModal] = useState(false);

  return (
    <>
      <Tooltip
        label={"Create New Directory"}
        position="bottom"
        transition="slide-up">
        <ToolBarButtonWrapper onClick={() => setOpenModal(true)}>
          <ToolbarIcon variant="subtle">
            <ThemeIcon
              variant="gradient"
              gradient={{ from: "indigo", to: "cyan" }}>
              <IconCirclePlus size={18} color={theme.white} />
            </ThemeIcon>
          </ToolbarIcon>
          <ToolbarButtonText>
            <Text color="blue">Directory</Text>
          </ToolbarButtonText>
        </ToolBarButtonWrapper>
      </Tooltip>

      <CreateDirectoryModal
        open={openModal}
        onClose={() => setOpenModal(false)}
      />
    </>
  );
}


이제 시도해보고 버튼을 클릭하면 모달이 열립니다.



양식을 갖도록 업데이트하거나 모달합시다.

// components/Toolbar/Modals/CreateDirectoryModal.tsx
import { Modal } from "@mantine/core";
import { Form } from "@mongez/react-form";
import SubmitButton from "design-system/components/Form/SubmitButton";
import TextInput from "design-system/components/Form/TextInput";
import { useKernel } from "../../../hooks";

export type CreateDirectoryModalProps = {
  open: boolean;
  onClose: () => void;
};

export default function CreateDirectoryModal({
  open,
  onClose,
}: CreateDirectoryModalProps) {
  const kernel = useKernel();
  return (
    <Modal
      title={<strong>{kernel.currentDirectoryNode?.path}</strong>}
      opened={open}
      trapFocus={false}
      onClose={onClose}>
      <Form>
        <h2>Create New Directory</h2>
        <TextInput
          name="name"
          required
          autoFocus
          placeholder="Please Enter Directory Name"
        />

        <div
          style={{
            textAlign: "end",
            marginTop: "1.5rem",
          }}>
          <SubmitButton>Create</SubmitButton>
        </div>
      </Form>
    </Modal>
  );
}


양식 제출 및 유효성 검사를 관리하기 위해 양식 구성 요소를 가져왔으며 양식을 만들기 위해 SubmitButtonTextInput를 가져왔습니다.

이제 다음과 같이 보입니다.



이제 Mantine 버튼을 사용하도록 SubmitButton UI를 업데이트하겠습니다.

// design-system/components/Form/SubmitButton.tsx
// 👇🏻 we import the button from Mantine
import { Button } from "@mantine/core";
import { useForm } from "@mongez/react-form";
import { useEffect, useState } from "react";
import Loader from "./../Indicators/Loader";

type SubmitButtonProps = {
  children: React.ReactNode;
  [key: string]: any;
};

export default function SubmitButton({
  children,
  ...props
}: SubmitButtonProps) {
  const [isSubmitting, submitting] = useState(false);
  const [isDisabled, disable] = useState(false);
  const formProvider = useForm();

  useEffect(() => {
    if (!formProvider) return;

    const onSubmit = formProvider.form.on("submit", () => {
      submitting(formProvider.form.isSubmitting());
      disable(formProvider.form.isSubmitting());
    });

    const inValidControls = formProvider.form.on("invalidControls", () => {
      disable(true);
    });

    const validControl = formProvider.form.on("validControls", () => {
      disable(false);
    });

    return () => {
      onSubmit.unsubscribe();
      validControl.unsubscribe();
      inValidControls.unsubscribe();
    };
  }, [formProvider]);

  return (
    <>
    // 👇🏻 we use the Mantine button instead of the BaseButton
      <Button
        // 👇🏻 we use the gradient variant
        variant="gradient"
        gradient={{ from: "blue", to: "cyan" }}
        type="submit"
        // 👇🏻 we use the loading prop to show the loader
        loading={isSubmitting}
        {...props}
        disabled={isDisabled || isSubmitting}>
        {children}
      </Button>    
    </>
  );
}


제출 버튼을 위한 것이므로 더 이상 수행하지 않습니다.

이제 Mantine 텍스트 입력을 사용하도록 텍스트 입력을 업데이트하겠습니다.

// design-system/components/Form/BaseInput.tsx
// 👇🏻 we import the Mantine text input
import { Input } from "@mantine/core";
import { FormInputProps, useFormInput } from "@mongez/react-form";
import { requiredRule } from "@mongez/validator";
import InputError from "./InputError";
import InputLabel from "./InputLabel";

export default function BaseInput(props: FormInputProps) {
  const {
    name,
    id,
    value,
    label,
    placeholder,
    required,
    onChange,
    onBlur,
    error,
    // 👇🏻 extract the autoFocus prop as well to allow focusing on the input
    autoFocus,
    otherProps,
  } = useFormInput(props);

  return (
    <>
      <div className="form-control">
        <InputLabel htmlFor={id} required={required}>
          {label}
        </InputLabel>
        // 👇🏻 we use the Mantine text input instead of the native input
        <Input
          id={id}
          name={name}
          placeholder={placeholder as string}
          onChange={onChange}
          onBlur={onBlur as any}
          value={value}
          // 👇🏻 we use the autoFocus prop to focus on the input
          autoFocus={autoFocus}
          // 👇🏻 we use the invalid prop to show the input has error
          invalid={error !== null}
          {...otherProps}
        />
        {error && <InputError error={error} />}
      </div>
    </>
  );
}

BaseInput.defaultProps = {
  type: "text",
  rules: [requiredRule],
};


이제 최종 모달 모양은 다음과 같습니다.



액션



이제 모달로 작업을 마쳤습니다. 이제 양식을 제출하고 디렉토리를 생성하겠습니다.

import { Modal } from "@mantine/core";
import { Form, FormInterface } from "@mongez/react-form";
import SubmitButton from "design-system/components/Form/SubmitButton";
import TextInput from "design-system/components/Form/TextInput";
import React from "react";
import { useKernel } from "../../../hooks";

export type CreateDirectoryModalProps = {
  open: boolean;
  onClose: () => void;
};

export default function CreateDirectoryModal({
  open,
  onClose,
}: CreateDirectoryModalProps) {
  const kernel = useKernel();

// 👇🏻 we use the onSubmit prop to submit the form
  const submitForm = (e: React.FormEvent, form: FormInterface) => {

  }

  return (
    <Modal
      title={<strong>{kernel.currentDirectoryNode?.path}</strong>}
      opened={open}
      trapFocus={false}
      onClose={onClose}>
      // 👇🏻 we use the onSubmit prop to submit the form
      <Form onSubmit={submitForm}>
        <h2>Create New Directory</h2>
        <TextInput
          name="name"
          required
          autoFocus
          placeholder="Please Enter Directory Name"
        />

        <div
          style={{
            textAlign: "end",
            marginTop: "1.5rem",
          }}>
          <SubmitButton>Create</SubmitButton>
        </div>
      </Form>
    </Modal>
  );
}


여기서 좋은 점은 입력이 채워질 때까지 양식 제출이 발생하지 않는다는 것입니다. 그렇지 않으면 오류가 표시됩니다.

이제 입력 값만 가져오면 되므로 전체 양식 요소를 보낼 필요가 없습니다.

// 👇🏻 we use the onSubmit prop to submit the form
  const submitForm = (e: React.FormEvent, form: FormInterface) => {
    const directoryName = form.value("name");

    kernel.actions.createDirectory(directoryName);
  };


따라서 입력name을 사용하여 입력 값을 얻을 수 있습니다. 이제 이를 createDirectory 작업에 전달하고 지금은 기록해 보겠습니다.

이제 양식을 제출하면 콘솔에 입력 값이 표시되므로 App를 입력하면 콘솔에 create directory App가 표시됩니다.

또 다른 작은 변경 사항을 만들어 보겠습니다. 이 디렉터리를 만드는 데 필요한 디렉터리 경로도 전달합니다.

// createDirectory.tsx
import Kernel from "../Kernel";

export default function createDirectory(kernel: Kernel) {
  return function create(directoryName: string, directoryPath: string = kernel.currentDirectoryNode?.path as string) {
    console.log("create directory", directoryName);
  };
}


따라서 두 번째 매개 변수에 디렉터리 경로를 전달할 수 있지만 현재 디렉터리 경로를 기본값으로 설정합니다.

다음 장



다음 장에서는 먼저 백엔드에 디렉토리를 만든 다음 React 프로젝트에서 구현합니다.

기사 저장소



Github Repository에서 챕터 파일을 볼 수 있습니다.

Don't forget the main branch has the latest updated code.



지금 어디 있는지 말해줘



이 시리즈를 저와 함께 후속 조치하는 경우 현재 위치와 어려움을 겪고 있는 부분을 알려주시면 최대한 도와드리겠습니다.

살람.

좋은 웹페이지 즐겨찾기