File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
08-14
08-14
08-18
08-14
08-18
08-18
File name
Commit message
Commit date
"use client";
import type { ReactNode, Ref } from "react";
import { cx } from "../../utils";
export interface FoxSelectOptionProps {
/** 목록을 여는 요소의 `aria-controls`가 가리킬 값. */
id?: string;
/** 목록에 이름을 주는 요소의 id — 보통 셀렉트의 라벨이다. */
labelledBy?: string;
/**
* 여러 항목을 동시에 고를 수 있는 목록(`aria-multiselectable`).
*
* 모양은 달라지지 않는다 — 고른 항목 표시는 항목이 각자 `aria-selected`로 하고, 이 속성은
* "몇 개까지 고를 수 있는지"를 보조기술에 알리는 것뿐이다. 이게 없으면 여러 개가 켜져
* 있어도 하나만 고르는 목록으로 읽혀, 두 번째를 고르면 첫 번째가 꺼진 줄 안다.
*/
multiselectable?: boolean;
/** `FoxSelectOptionItem`들. */
children?: ReactNode;
/** 배치 조정용. 모양이 달라야 하면 여기 말고 모디파이어를 추가한다. */
className?: string;
ref?: Ref<HTMLUListElement>;
}
/**
* @fox 셀렉트가 열렸을 때 뜨는 옵션 목록. 배치만 책임지고 상태를 갖지 않는다 —
* 열림 여부·활성 항목·선택 값은 전부 목록을 여는 쪽이 소유한다.
*
* 항목을 눌러도 포커스가 옮겨가지 않게 `mousedown` 기본 동작을 막는다. 콤보박스는 포커스가
* 여는 요소에 남아 있어야 `aria-activedescendant`가 성립하고, 막지 않으면 누르는 순간
* 포커스가 빠져 목록이 닫히면서 클릭이 성사되지 않는다.
*
* ⚠️ 스스로 위치를 잡지 못한다 — `position: absolute`라 위치 기준이 되는 조상이 필요하다.
* `FoxSelect` 안에서는 `fox-select__control`이 그 역할을 한다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxSelectOption({
id,
labelledBy,
multiselectable = false,
children,
className,
ref,
}: FoxSelectOptionProps) {
return (
<ul
ref={ref}
id={id}
role="listbox"
aria-labelledby={labelledBy}
// 기본값(하나만 고르는 목록)일 때는 속성을 아예 붙이지 않는다 — `false`를 적어 두는
// 것과 결과는 같지만, 없는 쪽이 DOM에서 읽기 쉽다.
aria-multiselectable={multiselectable || undefined}
className={cx("fox-select-option", className)}
onMouseDown={(event) => event.preventDefault()}
>
{children}
</ul>
);
}