React

Alert Dialog

Alert Dialog를 Stackflow와 함께 사용하는 방법을 안내합니다.

사용 가능 버전@seed-design/[email protected], @seed-design/[email protected]

Alert Dialog

Alert Dialog 컴포넌트에 대해 자세히 알아봅니다.

Making an Alert Dialog Activity

일반적인 경우 Alert Dialog를 Activity로 만들어 사용하는 것을 권장합니다.

  • Activity로 관리되므로, 하위 Activity보다 높고 상위 Activity보다는 낮은 z-index를 갖도록 관리하기 쉽습니다.
  • 딥링킹이 가능합니다. (URL 접속으로 Alert Dialog를 열 수 있습니다.)
  • @stackflow/plugin-basic-ui Modal에서의 마이그레이션이 쉽습니다.
Loading...

Usage

import { useActivityZIndexBase } from "@seed-design/stackflow";
import { useActivity, useFlow, type ActivityComponentType } from "@stackflow/react/future";
// ... more imports

const ActivityAlertDialog: ActivityComponentType<"ActivityAlertDialog"> = () => {
  const { pop, push } = useFlow();

  return (
    <AlertDialogRoot open={useActivity().isActive} onOpenChange={(open) => !open && pop()}>
      <AlertDialogContent layerIndex={useActivityZIndexBase()}>
        <AlertDialogHeader>
          <AlertDialogTitle>제목</AlertDialogTitle>
          <AlertDialogDescription>설명 텍스트</AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <VStack gap="x2">
            <AlertDialogAction asChild>
              <ActionButton>확인</ActionButton>
            </AlertDialogAction>
            <ActionButton
              variant="neutralSolid"
              onClick={() => push("AnotherActivity", {})}
            >
              다른 화면으로 이동
            </ActionButton>
          </VStack>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialogRoot>
  );
};
  1. open prop에 useActivity().isActive를 전달하여 Activity가 활성화될 때 Alert Dialog가 열리도록 합니다.
  2. onOpenChange를 통해 Alert Dialog가 닫힐 때 pop()을 실행하여 Activity를 종료합니다.
  3. layerIndex={useActivityZIndexBase()}로 Alert Dialog Activity의 z-index 기준점을 전달합니다.

Keeping Alert Dialog Mounted

Alert Dialog Activity 위에 다른 Activity를 push할 때 Alert Dialog가 unmount되는 것을 방지하려면,

  • open 상태를 isActive 대신 transitionState로 관리하고
  • modal prop을 isActive로 설정하고
  • onOpenChange 핸들러에서 !isOpen && isActive인 경우 pop()을 실행하도록 합니다.

이 패턴은 Alert Dialog 액티비티 위에 다른 오버레이 컴포넌트 액티비티를 중첩하여 표시하고 싶은 경우 유용합니다.

const { isActive, transitionState } = useActivity();

return (
  <AlertDialogRoot
    open={
      transitionState === "enter-active" || transitionState === "enter-done"
    }
    modal={isActive}
    onOpenChange={(open) => !open && isActive && pop()}
  >
    {/* ... */}
  </AlertDialogRoot>
);
  1. opentransitionState로 관리하여 다른 Activity가 위에 push되어도 Alert Dialog가 unmount되지 않도록 합니다.
  2. modal={isActive}로 Alert Dialog Activity가 비활성 상태일 때 modalfalse로 설정합니다. 이렇게 하지 않으면, 위에 push된 Activity에서 포커스 및 스크린 리더 접근이 동작하지 않습니다.
  3. onOpenChange 핸들러에서 isActive인 경우에만 pop()을 실행하여, 비활성 상태에서의 의도치 않은 Activity 종료를 방지합니다.

Syncing Alert Dialog State with a Step

Alert Dialog를 Activity로 만들 수 없는 경우, Alert Dialog가 표시된 상태를 Step으로 만들 수 있습니다.

  • 현재 Activity를 유지하면서도, 뒤로 가기 버튼 등으로 Alert Dialog를 닫을 수 있습니다.
  • AlertDialogTrigger를 사용하여 Alert Dialog를 열고 닫을 수 있습니다.
제약 사항

Activity로 만들지 않은 Alert Dialog에서 다른 Activity를 push하기 전, z-index 문제를 방지하기 위해 Alert Dialog를 닫으세요.

Alert Dialog를 닫을 수 없거나, Alert Dialog를 연 Activity로 돌아왔을 때 Alert Dialog가 열린 상태를 유지해야 하는 경우 Alert Dialog를 Activity로 만들어 사용하는 것을 권장합니다.

Activity 간 유려한 트랜지션을 제공하기 위해 하위 AppScreen 요소 중 일부가 상위 AppScreen 요소보다 위에 위치합니다. 이 제약으로 인해, 열린 상태의 Alert Dialog는 독립적인 Activity로 만들지 않는 경우 하위 Activity와 상위 Activity 사이에 위치시키는 것이 불가능합니다.

Loading...

Usage

import { useActivityZIndexBase } from "@seed-design/stackflow";
import { Portal } from "@seed-design/react";
import {
  useActivity,
  useActivityParams,
  useFlow,
  useStepFlow,
  type ActivityComponentType,
} from "@stackflow/react/future";
import { useEffect, useState } from "react";
// ... more imports

declare module "@stackflow/config" {
  interface Register {
    ActivityHome: {
      "alert-dialog"?: "open";
    };
  }
}

const ActivityHome: ActivityComponentType<"ActivityHome"> = () => {
  const [open, setOpen] = useState(false);

  const { push } = useFlow();
  const { pushStep, popStep } = useStepFlow("ActivityHome");
  const params = useActivityParams<"ActivityHome">();
  const isOverlayOpen = params["alert-dialog"] === "open";

  useEffect(() => {
    if (!isOverlayOpen) {
      setOpen(false);
    }
  }, [isOverlayOpen]);

  const onOpenChange = (newOpen: boolean) => {
    setOpen(newOpen);

    if (newOpen && !isOverlayOpen) {
      pushStep((params) => ({ ...params, "alert-dialog": "open" }));

      return;
    }

    if (!newOpen && isOverlayOpen) {
      popStep();

      return;
    }
  };

  return (
    <AppScreen>
      <AlertDialogRoot open={open} onOpenChange={onOpenChange}>
        <AlertDialogTrigger asChild>
          <ActionButton>Open</ActionButton>
        </AlertDialogTrigger>
        <Portal>
          <AlertDialogContent
            layerIndex={useActivityZIndexBase({ activityOffset: 1 })}
          >
            <AlertDialogHeader>
              <AlertDialogTitle>제목</AlertDialogTitle>
              <AlertDialogDescription>설명 텍스트</AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <HStack gap="x2">
                <ActionButton onClick={() => setOpen(false)}>취소</ActionButton>
                <ActionButton
                  onClick={() => {
                    setOpen(false); // 다른 Activity로 이동하기 전에는 Alert Dialog를 닫으세요.
                    push("ActivityNext");
                  }}
                >
                  다음
                </ActionButton>
              </HStack>
            </AlertDialogFooter>
          </AlertDialogContent>
        </Portal>
      </AlertDialogRoot>
    </AppScreen>
  );
};
  1. Portal을 사용하여 Alert Dialog가 DOM 상 현재 Activity 밖에 렌더링되도록 합니다.
  2. open prop를 관리하고, onOpenChange 핸들러를 통해 Step 상태와 동기화합니다.
  3. 뒤로 가기 버튼 등을 통해 Activity 파라미터가 변경될 때 Alert Dialog의 open 상태를 동기화합니다.
  4. layerIndex={useActivityZIndexBase({ activityOffset: 1 })}로 현재 Activity보다 한 단계 높은 z-index 기준점을 전달합니다.

useStepOverlay

#2와 #3을 일반화하여 useStepOverlay를 사용하면 편리합니다. useStepOverlay 구현 예시는 코드를 참고하세요.

import { useActivityZIndexBase } from "@seed-design/stackflow";
import { Portal } from "@seed-design/react";
import { useStepOverlay } from "./use-step-overlay";
// ... more imports

const MyActivity: ActivityComponentType = () => {
  const { overlayProps, setOpen } = useStepOverlay();
  const { popStep } = useStepFlow("MyActivity");
  const { push } = useFlow();

  return (
    <AppScreen>
      <AlertDialogRoot {...overlayProps}>
        <AlertDialogTrigger asChild>
          <ActionButton>Open</ActionButton>
        </AlertDialogTrigger>
        <Portal>
          <AlertDialogContent
            layerIndex={useActivityZIndexBase({ activityOffset: 1 })}
          >
            <AlertDialogHeader>
              <AlertDialogTitle>제목</AlertDialogTitle>
              <AlertDialogDescription>설명 텍스트</AlertDialogDescription>
            </AlertDialogHeader>
            <AlertDialogFooter>
              <HStack gap="x2">
                <ActionButton onClick={() => setOpen(false)}>취소</ActionButton>
                <ActionButton
                  onClick={() => {
                    setOpen(false); // 다른 Activity로 이동하기 전에는 Alert Dialog를 닫으세요.
                    push("ActivityNext");
                  }}
                >
                  다음
                </ActionButton>
              </HStack>
            </AlertDialogFooter>
          </AlertDialogContent>
        </Portal>
      </AlertDialogRoot>
    </AppScreen>
  );
};

About useActivityZIndexBase

useActivityZIndexBase는 각 Activity의 z-index 기준점을 반환하는 훅입니다.

Last updated on

On this page