Components

Popover

사용 예시

설정 모음

자주 바꾸는 설정을 모아 둘 때 써요. 트리거 버튼에는 열리는 내용의 이름을 적어요.

tsx
<Popover>  <PopoverTrigger render={<Button color="neutral" emphasis="outline" />}>    <ListFilter />    필터  </PopoverTrigger>  <PopoverContent className="w-56" aria-label="필터">    <div className="flex flex-col gap-stack">      <Field>        <FieldLabel>최근 며칠</FieldLabel>        <NumberField value={days} onValueChange={setDays} decrementLabel="기간 줄이기" incrementLabel="기간 늘리기" min={1} max={30} size="sm" />      </Field>      <Field layout="split">        <FieldLabel>완료한 항목 포함</FieldLabel>        <Switch checked={withDone} onCheckedChange={setWithDone} />      </Field>    </div>  </PopoverContent></Popover>

고르면 닫기

고른 값이 바로 적용될 때 써요. 버튼을 PopoverClose로 감싸면 onClick이 실행된 뒤 Popover가 닫혀요.

tsx
<Popover>  <PopoverTrigger render={<Button color="neutral" emphasis="outline" className="w-36" />}>    <Users />    {scope}  </PopoverTrigger>  <PopoverContent className="w-56" aria-label="보이는 범위">    <div className="flex flex-col gap-stack">      <div className="grid grid-cols-2 gap-2">        {scopes.map(s => (          <PopoverClose            key={s}            render={(              <Button                color="neutral" emphasis="outline"                size="sm"                className="w-full"                onClick={() => setScope(s)}              />            )}          >            {s}          </PopoverClose>        ))}      </div>      <Field>        <FieldLabel>만료일</FieldLabel>        <Input value={expiry} onChange={e => setExpiry(e.target.value)} placeholder="9월 30일" size="sm" />      </Field>    </div>  </PopoverContent></Popover>

PopoverAnchor

PopoverContent를 트리거보다 넓은 영역에 맞춰 열 때 써요. PopoverContent가 PopoverAnchor만큼 넓으면 화살표가 가리킬 곳이 없어서 arrow를 false로 둬요.

tsx
<Popover>  <PopoverAnchor className="flex w-full max-w-80 items-center gap-2">    <Input value={keyword} onChange={e => setKeyword(e.target.value)} placeholder="이름으로 찾기" />    <PopoverTrigger render={<Button color="neutral" emphasis="outline" shape="square" aria-label="필터 조건" />}>      <SlidersHorizontal />    </PopoverTrigger>  </PopoverAnchor>  <PopoverContent align="start" arrow={false} className="w-80" aria-label="필터 조건">    <div className="flex flex-col gap-stack">      <Field layout="split">        <FieldLabel>보관함 포함</FieldLabel>        <Switch checked={withArchived} onCheckedChange={setWithArchived} />      </Field>    </div>  </PopoverContent></Popover>

뜨는 방향

tsx
<Popover>  <PopoverTrigger render={<Button color="neutral" emphasis="outline" size="sm" />}>top</PopoverTrigger>  <PopoverContent side="top" className="w-44" aria-label="뜨는 방향">    <Field layout="split">      <FieldLabel>즐겨찾기만</FieldLabel>      <Switch checked={starredOnly} onCheckedChange={setStarredOnly} />    </Field>  </PopoverContent></Popover>

호출하는 쪽에서 여닫기

트리거 말고 다른 버튼으로도 열어야 할 때 써요. 트리거를 눌러도 그대로 열려요.

tsx
<Popover open={filterOpen} onOpenChange={setFilterOpen}>  <PopoverTrigger render={<Button color="neutral" emphasis="outline" />}>    <ListFilter />    필터  </PopoverTrigger>  <PopoverContent className="w-56" aria-label="필터">    <Field>      <FieldLabel>최근 며칠</FieldLabel>      <NumberField value={days} onValueChange={setDays} decrementLabel="기간 줄이기" incrementLabel="기간 늘리기" min={1} max={30} size="sm" />    </Field>  </PopoverContent></Popover><Callout color="warning" title="보이는 항목이 없어요">  필터 조건을 확인해 보세요.  <Button color="neutral" emphasis="ghost" size="sm" onClick={() => setFilterOpen(true)}>    필터 열기  </Button></Callout>

Trigger 없이 열기

달력의 날짜, 표의 셀처럼 누른 곳마다 기준이 다르면 open과 anchor로 열어요. anchor에는 요소, ref, getBoundingClientRect를 가진 가상 요소, 그 값을 돌려주는 함수를 넘길 수 있어요. 바깥을 누르거나 Esc를 누르면 닫혀요.

tsx
const [picked, setPicked] = useState<{ el: HTMLElement, date: number } | null>(null){week.map(d => (  <button key={d.date} type="button" onClick={e => setPicked({ el: e.currentTarget, date: d.date })}>    {d.day} {d.date}  </button>))}<Popover open={picked !== null} onOpenChange={open => !open && setPicked(null)}>  <PopoverContent anchor={picked?.el} side="bottom" aria-label="일정">…</PopoverContent></Popover><PopoverContent anchor={{ getBoundingClientRect: () => DOMRect.fromRect({ x, y, width: 0, height: 0 }) }}>  …</PopoverContent>

컨트롤

값7
tsx
<Popover>  <PopoverTrigger render={<Button color="neutral" emphasis="outline" />}>필터</PopoverTrigger>  <PopoverContent className="w-44" aria-label="필터">    <Field>      <FieldLabel>최근 며칠</FieldLabel>      <NumberField value={days} onValueChange={setDays} decrementLabel="기간 줄이기" incrementLabel="기간 늘리기" min={1} max={30} size="sm" />    </Field>  </PopoverContent></Popover>

명세

이름
이름값기본
Popover
openboolean-
호출하는 쪽에서 열림 상태를 관리해요.
defaultOpenbooleanfalse
처음에 열어 둘지 정해요.
onOpenChange(open: boolean, eventDetails) => void-
열리거나 닫히면 호출돼요.
renderReactElement-
기본 요소 대신 그릴 요소예요. PopoverTrigger와 PopoverClose에 Button을 넘길 때 써요.
classNamestring-
PopoverContent
sidetop, right, bottom, leftbottom
지정한 쪽에 공간이 없으면 반대쪽에 떠요.
alignstart, center, endcenter
트리거를 기준으로 PopoverContent를 맞추는 위치예요.
arrowbooleantrue
트리거나 PopoverAnchor를 가리키는 화살표를 보여요.
anchorElement, ref, 가상 요소, 함수-
트리거 대신 기준으로 삼을 곳이에요. getBoundingClientRect를 가진 객체를 넘기면 화면 좌표 사각형을 기준으로 열어요. PopoverAnchor보다 먼저예요.
sideOffsetnumber, 함수14, arrow가 없으면 8
기준과 떨어진 거리예요. 단위는 px이에요.
alignOffsetnumber, 함수0
align 방향으로 옮기는 거리예요. 단위는 px이에요.
collisionAvoidanceobject-
화면 가장자리에 닿을 때 뒤집을지, 밀어 넣을지 정해요. Base UI Positioner의 값을 그대로 받아요.