Alert Dialog
Alert Dialog를 Stackflow와 함께 사용하는 방법을 안내합니다.
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에서의 마이그레이션이 쉽습니다.
import { VStack } from "@seed-design/react";
import { useFlow, type ActivityComponentType } from "@stackflow/react/future";
import { AppBar, AppBarBackButton, AppBarLeft, AppBarMain } from "seed-design/ui/app-bar";
import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen";
import { ActionButton } from "seed-design/ui/action-button";
declare module "@stackflow/config" {
interface Register {
ActivityAlertDialogActivity: {};
}
}
const ActivityAlertDialogActivity: ActivityComponentType<"ActivityAlertDialogActivity"> = () => {
const { push } = useFlow();
return (
<AppScreen>
<AppBar>
<AppBarLeft>
<AppBarBackButton />
</AppBarLeft>
<AppBarMain title="Activity" />
</AppBar>
<AppScreenContent>
<VStack p="x5" justify="center" gap="x4">
<ActionButton
variant="neutralSolid"
flexGrow
onClick={() => push("ActivityAlertDialog", {})}
>
ActivityAlertDialog를 Push
</ActionButton>
<ActionButton
variant="neutralWeak"
flexGrow
onClick={() => push("ActivityAlertDialogActivity", {})}
>
지금 열린 이 Activity를 Push
</ActionButton>
</VStack>
</AppScreenContent>
</AppScreen>
);
};
export default ActivityAlertDialogActivity;import { useActivity, useFlow, type ActivityComponentType } from "@stackflow/react/future";
import { ActionButton } from "seed-design/ui/action-button";
import {
AlertDialogAction,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogRoot,
AlertDialogTitle,
} from "seed-design/ui/alert-dialog";
import { ResponsivePair } from "@seed-design/react";
import { send } from "@stackflow/compat-await-push";
import { useActivityZIndexBase } from "@seed-design/stackflow";
declare module "@stackflow/config" {
interface Register {
ActivityAlertDialog: {};
}
}
const ActivityAlertDialog: ActivityComponentType<"ActivityAlertDialog"> = () => {
const activity = useActivity();
const { pop, push } = useFlow();
const handleClose = (open: boolean) => {
if (!open) {
pop();
send({
activityId: activity.id,
data: {
message: "hello",
},
});
}
};
return (
<AlertDialogRoot open={activity.isActive} onOpenChange={handleClose}>
<AlertDialogContent layerIndex={useActivityZIndexBase()}>
<AlertDialogHeader>
<AlertDialogTitle>제목</AlertDialogTitle>
<AlertDialogDescription>다람쥐 헌 쳇바퀴에 타고파</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<ResponsivePair gap="x2">
<AlertDialogAction asChild>
<ActionButton variant="neutralWeak">확인</ActionButton>
</AlertDialogAction>
<ActionButton
variant="neutralSolid"
onClick={() =>
push("ActivityDetail", {
title: "AlertDialog에서 Push됨",
body: "다람쥐 헌 쳇바퀴에 타고파",
})
}
>
Push
</ActionButton>
</ResponsivePair>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogRoot>
);
};
export default ActivityAlertDialog;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>
);
};openprop에useActivity().isActive를 전달하여 Activity가 활성화될 때 Alert Dialog가 열리도록 합니다.onOpenChange를 통해 Alert Dialog가 닫힐 때pop()을 실행하여 Activity를 종료합니다.layerIndex={useActivityZIndexBase()}로 Alert Dialog Activity의 z-index 기준점을 전달합니다.
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 사이에 위치시키는 것이 불가능합니다.
import { HStack, Portal, VStack } from "@seed-design/react";
import { useActivityZIndexBase } from "@seed-design/stackflow";
import {
useActivityParams,
useFlow,
useStepFlow,
type ActivityComponentType,
} from "@stackflow/react/future";
import { useEffect, useState } from "react";
import { ActionButton } from "seed-design/ui/action-button";
import { AppBar, AppBarMain } from "seed-design/ui/app-bar";
import { AppScreen, AppScreenContent } from "seed-design/ui/app-screen";
import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogRoot,
AlertDialogTitle,
AlertDialogTrigger,
} from "seed-design/ui/alert-dialog";
declare module "@stackflow/config" {
interface Register {
ActivityAlertDialogStep: {
"alert-dialog"?: "open";
};
}
}
const ActivityAlertDialogStep: ActivityComponentType<"ActivityAlertDialogStep"> = () => {
const [open, setOpen] = useState(false);
const { push } = useFlow();
const { pushStep, popStep } = useStepFlow("ActivityAlertDialogStep");
const params = useActivityParams<"ActivityAlertDialogStep">();
const isOverlayOpen = params["alert-dialog"] === "open";
useEffect(() => {
if (!isOverlayOpen) {
setOpen(false);
}
if (isOverlayOpen) {
setOpen(true);
}
}, [isOverlayOpen]);
const onOpenChange = (newOpen: boolean) => {
setOpen(newOpen);
if (newOpen && !isOverlayOpen) {
pushStep((params) => ({ ...params, "alert-dialog": "open" }));
return;
}
if (!newOpen && isOverlayOpen) {
popStep();
return;
}
};
return (
<AppScreen>
<AppBar>
<AppBarMain title="Step" />
</AppBar>
<AppScreenContent>
<AlertDialogRoot open={open} onOpenChange={onOpenChange}>
<AlertDialogTrigger asChild>
<VStack p="x5" justify="center" gap="x4">
<ActionButton variant="neutralSolid" flexGrow>
Alert Dialog 열기
</ActionButton>
</VStack>
</AlertDialogTrigger>
<Portal>
<AlertDialogContent layerIndex={useActivityZIndexBase({ activityOffset: 1 })}>
<AlertDialogHeader>
<AlertDialogTitle>Step</AlertDialogTitle>
<AlertDialogDescription>
Alert Dialog가 Step으로 만들어져 있기 때문에 뒤로 가기로 닫을 수 있습니다.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<HStack gap="x2">
<ActionButton onClick={() => popStep()} variant="neutralWeak">
닫기
</ActionButton>
<ActionButton
flexGrow
variant="neutralSolid"
onClick={() => {
// 이 Alert Dialog는 Activity로 만들어지지 않았기 때문에, z-index 정리를 위해
// Alert Dialog를 먼저 닫고 다음 Activity를 push해야 합니다.
popStep();
push("ActivityDetail", {
title: "Alert Dialog에서 이동한 화면",
body: "Alert Dialog를 닫고 이동했습니다.",
});
}}
>
Push
</ActionButton>
</HStack>
</AlertDialogFooter>
</AlertDialogContent>
</Portal>
</AlertDialogRoot>
</AppScreenContent>
</AppScreen>
);
};
export default ActivityAlertDialogStep;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={() => popStep()}>취소</ActionButton>
<ActionButton
onClick={() => {
popStep(); // 다른 Activity로 이동하기 전에는 Alert Dialog를 닫으세요.
push("ActivityNext");
}}
>
다음
</ActionButton>
</HStack>
</AlertDialogFooter>
</AlertDialogContent>
</Portal>
</AlertDialogRoot>
</AppScreen>
);
};Portal을 사용하여 Alert Dialog가 DOM 상 현재 Activity 밖에 렌더링되도록 합니다.openprop를 관리하고,onOpenChange핸들러를 통해 Step 상태와 동기화합니다.- 뒤로 가기 버튼 등을 통해 Activity 파라미터가 변경될 때 Alert Dialog의
open상태를 동기화합니다. 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={() => popStep()}>취소</ActionButton>
<ActionButton
onClick={() => {
popStep(); // 다른 Activity로 이동하기 전에는 Alert Dialog를 닫으세요.
push("ActivityNext");
}}
>
다음
</ActionButton>
</HStack>
</AlertDialogFooter>
</AlertDialogContent>
</Portal>
</AlertDialogRoot>
</AppScreen>
);
};About useActivityZIndexBase
useActivityZIndexBase는 각 Activity의 z-index 기준점을 반환하는 훅입니다.
Last updated on