refactor: FoxInput·FoxTextArea·FoxSelect의 라벨을 FoxFormLabel로 교체
@8d37131c4147dc444bdc8772e01e4dd7cb1bed7f
--- @fox/core/components/fox-input/fox-input.tsx
+++ @fox/core/components/fox-input/fox-input.tsx
... | ... | @@ -10,6 +10,7 @@ |
| 10 | 10 |
type ReactNode, |
| 11 | 11 |
} from "react"; |
| 12 | 12 |
import { cx } from "../../utils";
|
| 13 |
+import { FoxFormLabel } from "../fox-form-label";
|
|
| 13 | 14 |
|
| 14 | 15 |
export type FoxInputSize = "lg" | "md" | "sm"; |
| 15 | 16 |
export type FoxInputState = "default" | "focused" | "completed" | "error" | "disabled" | "view"; |
... | ... | @@ -137,9 +138,7 @@ |
| 137 | 138 |
data-state={state}
|
| 138 | 139 |
> |
| 139 | 140 |
{label && (
|
| 140 |
- <label className="fox-input__label" htmlFor={fieldId}>
|
|
| 141 |
- {label}
|
|
| 142 |
- </label> |
|
| 141 |
+ <FoxFormLabel htmlFor={fieldId}>{label}</FoxFormLabel>
|
|
| 143 | 142 |
)} |
| 144 | 143 |
|
| 145 | 144 |
<div className="fox-input__box"> |
--- @fox/core/components/fox-select/fox-select.tsx
+++ @fox/core/components/fox-select/fox-select.tsx
... | ... | @@ -10,6 +10,7 @@ |
| 10 | 10 |
type Ref, |
| 11 | 11 |
} from "react"; |
| 12 | 12 |
import { cx } from "../../utils";
|
| 13 |
+import { FoxFormLabel, type FoxFormLabelRequirement } from "../fox-form-label";
|
|
| 13 | 14 |
import { FoxSelectOption } from "../fox-select-option";
|
| 14 | 15 |
import { FoxSelectOptionItem } from "../fox-select-option-item";
|
| 15 | 16 |
|
... | ... | @@ -60,6 +61,8 @@ |
| 60 | 61 |
readOnly?: boolean; |
| 61 | 62 |
/** 필드 위 라벨. 넘기지 않으면 라벨을 렌더링하지 않는다. */ |
| 62 | 63 |
label?: ReactNode; |
| 64 |
+ /** 라벨 뒤 필수·선택 표시. `label`이 없으면 의미 없다. */ |
|
| 65 |
+ requirement?: FoxFormLabelRequirement; |
|
| 63 | 66 |
/** select 아래 힌트 메시지. 넘기지 않으면 힌트 영역을 렌더링하지 않는다. */ |
| 64 | 67 |
hint?: ReactNode; |
| 65 | 68 |
/** 힌트 메시지 앞에 붙는 아이콘. `hint`가 없으면 의미 없다. */ |
... | ... | @@ -102,6 +105,7 @@ |
| 102 | 105 |
disabled = false, |
| 103 | 106 |
readOnly = false, |
| 104 | 107 |
label, |
| 108 |
+ requirement, |
|
| 105 | 109 |
hint, |
| 106 | 110 |
hintIcon, |
| 107 | 111 |
id, |
... | ... | @@ -232,9 +236,9 @@ |
| 232 | 236 |
onMouseLeave={onHoverChange ? () => onHoverChange(false) : undefined}
|
| 233 | 237 |
> |
| 234 | 238 |
{label ? (
|
| 235 |
- <label id={labelId} htmlFor={triggerId} className="fox-select__label">
|
|
| 239 |
+ <FoxFormLabel id={labelId} htmlFor={triggerId} requirement={requirement}>
|
|
| 236 | 240 |
{label}
|
| 237 |
- </label> |
|
| 241 |
+ </FoxFormLabel> |
|
| 238 | 242 |
) : null} |
| 239 | 243 |
|
| 240 | 244 |
<div className="fox-select__control"> |
--- @fox/core/components/fox-text-area/fox-text-area.tsx
+++ @fox/core/components/fox-text-area/fox-text-area.tsx
... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 |
type ReactNode, |
| 10 | 10 |
} from "react"; |
| 11 | 11 |
import { cx } from "../../utils";
|
| 12 |
+import { FoxFormLabel } from "../fox-form-label";
|
|
| 12 | 13 |
|
| 13 | 14 |
export type FoxTextAreaState = "default" | "focused" | "completed" | "error" | "disabled" | "view"; |
| 14 | 15 |
|
... | ... | @@ -95,9 +96,7 @@ |
| 95 | 96 |
return ( |
| 96 | 97 |
<div className={cx("fox-text-area", className)} data-state={state}>
|
| 97 | 98 |
{label && (
|
| 98 |
- <label className="fox-text-area__label" htmlFor={fieldId}>
|
|
| 99 |
- {label}
|
|
| 100 |
- </label> |
|
| 99 |
+ <FoxFormLabel htmlFor={fieldId}>{label}</FoxFormLabel>
|
|
| 101 | 100 |
)} |
| 102 | 101 |
|
| 103 | 102 |
<div className="fox-text-area__box"> |
--- @fox/dev-test/component-registry.tsx
+++ @fox/dev-test/component-registry.tsx
... | ... | @@ -984,6 +984,16 @@ |
| 984 | 984 |
</div> |
| 985 | 985 |
), |
| 986 | 986 |
}, |
| 987 |
+ {
|
|
| 988 |
+ label: "requirement — 라벨을 FoxFormLabel이 그린다", |
|
| 989 |
+ node: ( |
|
| 990 |
+ <div className={styles.demoFields}>
|
|
| 991 |
+ <FoxSelect size="md" label="표시 없음" options={SELECT_ITEMS} />
|
|
| 992 |
+ <FoxSelect size="md" label="필수" options={SELECT_ITEMS} requirement="required" />
|
|
| 993 |
+ <FoxSelect size="md" label="선택" options={SELECT_ITEMS} requirement="optional" />
|
|
| 994 |
+ </div> |
|
| 995 |
+ ), |
|
| 996 |
+ }, |
|
| 987 | 997 |
], |
| 988 | 998 |
}, |
| 989 | 999 |
{
|
--- @fox/styles/_fox-input.scss
+++ @fox/styles/_fox-input.scss
... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 |
// |
| 4 | 4 |
// 마크업 계약 (React 밖 소비자용): |
| 5 | 5 |
// <div class="fox-input fox-input--md"> |
| 6 |
-// <label class="fox-input__label" for="…">레이블</label> |
|
| 6 |
+// <label class="fox-form-label" for="…">레이블</label> |
|
| 7 | 7 |
// <div class="fox-input__box"> |
| 8 | 8 |
// <input class="fox-input__field" id="…" placeholder="…"> |
| 9 | 9 |
// <button class="fox-input__clear" type="button">…svg…</button> <!-- 선택 --> |
... | ... | @@ -35,10 +35,6 @@ |
| 35 | 35 |
|
| 36 | 36 |
.fox-input {
|
| 37 | 37 |
@include field.root; |
| 38 |
- |
|
| 39 |
- &__label {
|
|
| 40 |
- @include field.label; |
|
| 41 |
- } |
|
| 42 | 38 |
|
| 43 | 39 |
// 크기(높이·좌우 여백·모서리)는 아래 크기 수식어가 정한다. |
| 44 | 40 |
&__box {
|
--- @fox/styles/_fox-select.scss
+++ @fox/styles/_fox-select.scss
... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 |
// |
| 6 | 6 |
// 마크업 계약 (React 밖 소비자용): |
| 7 | 7 |
// <div class="fox-select fox-select--md"> |
| 8 |
-// <label class="fox-select__label" for="sel">라벨</label> <!-- 선택 --> |
|
| 8 |
+// <label class="fox-form-label" for="sel">라벨</label> <!-- 선택 --> |
|
| 9 | 9 |
// <div class="fox-select__control"> <!-- 리스트박스 위치 기준 --> |
| 10 | 10 |
// <button class="fox-select__trigger fox-select__trigger--completed" id="sel" |
| 11 | 11 |
// type="button" role="combobox" aria-haspopup="listbox" |
... | ... | @@ -36,8 +36,11 @@ |
| 36 | 36 |
// |
| 37 | 37 |
// 폭은 정하지 않는다 — 부모가 정한다. 높이만 `size`가 정한다. |
| 38 | 38 |
// |
| 39 |
-// 라벨·옵션·힌트는 `size`와 무관하게 공통 스타일이다. 크기 모디파이어는 트리거와 트리거 |
|
| 40 |
-// 아이콘만 바꾼다 — 드롭다운 목록은 어느 크기에서 열려도 같은 서체로 읽혀야 한다. |
|
| 39 |
+// 라벨은 `fox-form-label`이 갖는다 — 시안에서 입력 계열과 같은 공용 조각이라 값이 갈라질 |
|
| 40 |
+// 이유가 없고, 필수·선택 표시도 그쪽에 붙는다. 이 파일에는 라벨 규칙이 없다. |
|
| 41 |
+// |
|
| 42 |
+// 옵션·힌트는 `size`와 무관하게 공통 스타일이다. 크기 모디파이어는 트리거와 트리거 아이콘만 |
|
| 43 |
+// 바꾼다 — 드롭다운 목록은 어느 크기에서 열려도 같은 서체로 읽혀야 한다. |
|
| 41 | 44 |
// |
| 42 | 45 |
// 토큰이 아닌 값(FoxButton과 동일한 근거로 승인된 항목): |
| 43 | 46 |
// - line-height / letter-spacing: 시안 텍스트 스타일의 값이나 Figma가 변수로 export하지 않는다. |
... | ... | @@ -49,13 +52,6 @@ |
| 49 | 52 |
display: flex; |
| 50 | 53 |
flex-direction: column; |
| 51 | 54 |
gap: fox.gap(4); |
| 52 |
- |
|
| 53 |
- &__label {
|
|
| 54 |
- color: fox.color(font-neutral-default); |
|
| 55 |
- font-family: fox.font-family(body); |
|
| 56 |
- font-size: fox.font-size(label-md); |
|
| 57 |
- font-weight: fox.font-weight(medium); |
|
| 58 |
- } |
|
| 59 | 55 |
|
| 60 | 56 |
&__control {
|
| 61 | 57 |
position: relative; |
--- @fox/styles/_fox-text-area.scss
+++ @fox/styles/_fox-text-area.scss
... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 |
// |
| 4 | 4 |
// 마크업 계약 (React 밖 소비자용): |
| 5 | 5 |
// <div class="fox-text-area"> |
| 6 |
-// <label class="fox-text-area__label" for="…">레이블</label> |
|
| 6 |
+// <label class="fox-form-label" for="…">레이블</label> |
|
| 7 | 7 |
// <div class="fox-text-area__box"> |
| 8 | 8 |
// <textarea class="fox-text-area__field" id="…" rows="5" placeholder="…"></textarea> |
| 9 | 9 |
// </div> |
... | ... | @@ -37,10 +37,6 @@ |
| 37 | 37 |
|
| 38 | 38 |
.fox-text-area {
|
| 39 | 39 |
@include field.root; |
| 40 |
- |
|
| 41 |
- &__label {
|
|
| 42 |
- @include field.label; |
|
| 43 |
- } |
|
| 44 | 40 |
|
| 45 | 41 |
// 여러 줄이라 세로 가운데 정렬을 끄고, 크기 축이 없어 md 여백을 직접 준다. |
| 46 | 42 |
&__box {
|
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?