임동욱 임동욱 08-19
feat: FoxInput 필수 표시 · FoxChipArea 줄바꿈
둘 다 시안 ADM_ADM_102_p(5227:2035)가 요구하는데 없던 것이다.

- `FoxInput`에 `requirement`를 열었다. 라벨을 FoxFormLabel로 그리면서 그 값을 넘기지 않아
  `이름*`처럼 필수 별표가 붙은 시안을 그릴 수 없었다. 이미 같은 prop을 가진 `FoxSelect`와
  같은 모양으로 맞췄다.
- `FoxChipArea`에 `wrap`을 열었다. 스타일 주석이 "칩이 여러 줄로 흘러야 하는 자리가 나오면
  시안에 먼저 추가한다"고 적어 둔 그 자리다 — 시안의 "메뉴 선택"이 칩을 두 줄로 감는다.
  기본값은 종전대로 한 줄이라 기존 호출부는 그대로다. `FoxButtonGroup`의 같은 prop과 맞췄다.

Co-Authored-By: Claude Opus 5 
@d8a58cd67c2a5ee17215c95659c6baf6f1159a6f
@fox/core/components/fox-chip-area/fox-chip-area.tsx
--- @fox/core/components/fox-chip-area/fox-chip-area.tsx
+++ @fox/core/components/fox-chip-area/fox-chip-area.tsx
@@ -24,6 +24,8 @@
   label?: string;
   /** 묶음 이름 역할을 하는 요소의 id. `label`보다 우선한다 — 화면의 글자와 어긋나지 않는다. */
   labelledBy?: string;
+  /** 칩이 한 줄에 다 들어가지 않으면 다음 줄로 흘린다(`FoxButtonGroup`과 같은 규약). */
+  wrap?: boolean;
   /** `FoxChip`들. */
   children?: ReactNode;
   /** 배치 조정용. */
@@ -77,6 +79,7 @@
   size,
   label,
   labelledBy,
+  wrap = false,
   children,
   className,
   ref,
@@ -93,7 +96,7 @@
       role="group"
       aria-label={labelledBy ? undefined : label}
       aria-labelledby={labelledBy}
-      className={cx("fox-chip-area", className)}
+      className={cx("fox-chip-area", wrap && "fox-chip-area--wrap", className)}
     >
       {overrideChildren(children, overrides)}
     </div>
@fox/core/components/fox-input/fox-input.tsx
--- @fox/core/components/fox-input/fox-input.tsx
+++ @fox/core/components/fox-input/fox-input.tsx
@@ -10,7 +10,7 @@
   type ReactNode,
 } from "react";
 import { cx } from "../../utils";
-import { FoxFormLabel } from "../fox-form-label";
+import { FoxFormLabel, type FoxFormLabelRequirement } from "../fox-form-label";
 
 export type FoxInputSize = "lg" | "md" | "sm";
 export type FoxInputState = "default" | "focused" | "completed" | "error" | "disabled" | "view";
@@ -31,6 +31,8 @@
   onChange?: (value: string, event?: ChangeEvent<HTMLInputElement>) => void;
   onInput?: (value: string, event: FormEvent<HTMLInputElement>) => void;
   label?: string;
+  /** 라벨 뒤 필수·선택 표시. `label`이 없으면 의미 없다(`FoxSelect`와 같은 규약). */
+  requirement?: FoxFormLabelRequirement;
   /** 헬퍼 메시지. 색은 상태를 따라간다. */
   message?: string;
   /** 헬퍼 메시지 앞 아이콘. `currentColor`로 그려야 상태 색을 따라간다. */
@@ -70,6 +72,7 @@
   onChange,
   onInput,
   label,
+  requirement,
   message,
   messageIcon,
   icon,
@@ -138,7 +141,9 @@
       data-state={state}
     >
       {label && (
-        <FoxFormLabel htmlFor={fieldId}>{label}</FoxFormLabel>
+        <FoxFormLabel htmlFor={fieldId} requirement={requirement}>
+          {label}
+        </FoxFormLabel>
       )}
 
       <div className="fox-input__box">
@fox/styles/_fox-chip-area.scss
--- @fox/styles/_fox-chip-area.scss
+++ @fox/styles/_fox-chip-area.scss
@@ -15,8 +15,8 @@
 // `align-items: center`가 필요한 이유는 칩의 크기(높이)가 섞일 수 있어서다. `FoxChipArea`의
 // `size`로 통일하면 섞이지 않지만, 통일하지 않고 쓰는 것도 막지 않는다.
 //
-// ⚠️ 줄바꿈이 없다(`flex-wrap`을 주지 않는다) — 시안이 한 줄만 그린다. 폭이 모자라면 칩이
-// 눌리는 게 아니라 넘친다. 칩이 여러 줄로 흘러야 하는 자리가 나오면 시안에 먼저 추가한다.
+// 기본은 한 줄이다 — 폭이 모자라면 칩이 눌리는 게 아니라 넘친다. 여러 줄로 흘러야 하는 자리는
+// `--wrap`을 건다(시안 ADM_ADM_102_p의 "메뉴 선택"이 칩을 두 줄로 감는다).
 
 @use "@fox/styles/abstracts" as fox;
 
@@ -24,4 +24,8 @@
   display: inline-flex;
   align-items: center;
   gap: fox.gap(2);
+
+  &--wrap {
+    flex-wrap: wrap;
+  }
 }
Add a comment
List