• Y
  • List All
  • Feedback
    • This Project
    • All Projects
Profile Account settings Log out
  • Favorite
  • Project
  • All
Loading...
  • Log in
  • Sign up
nbnloper / edupay-admin star
Forked from ldu0009 / edupay-admin
  • Project homeH
  • CodeC
  • IssueI
  • Pull requestP
  • Review R
  • MilestoneM
  • BoardB
  • Files
  • Commit
  • Branches
edupay-admin@foxdev-testcomponent-registry.tsx
Download as .zip file
File name
Commit message
Commit date
@fox
feat: FoxInput 추가
08-13
app
refactor: 컴포넌트 스타일을 전역 BEM으로 전환 — 프레임워크 무관 사용 가능
08-12
components/ui
feat: 스플래시 제거
08-11
lib
feat: 스플래시 제거
08-11
public
chore: WantedSans 폰트·FoxPay 로고 반입 및 파비콘 교체
07-27
.gitignore
chore: 클로드 관련 파일 git 추적 제외
07-27
README.md
Initial commit from Create Next App
07-27
eslint.config.mjs
Initial commit from Create Next App
07-27
next.config.ts
build: Tailwind 제거하고 Sass 파이프라인 도입
08-11
package-lock.json
build: Tailwind 제거하고 Sass 파이프라인 도입
08-11
package.json
build: Tailwind 제거하고 Sass 파이프라인 도입
08-11
proxy.ts
feat: /dev-test/design 라우트 추가
08-11
tsconfig.json
build: Tailwind 제거하고 Sass 파이프라인 도입
08-11
File name
Commit message
Commit date
core
feat: FoxInput 추가
08-13
dev-test
feat: FoxInput 추가
08-13
styles
feat: FoxInput 추가
08-13
tools
refactor: 토큰 매니페스트 제거
08-11
README.md
feat: FoxInput 추가
08-13
File name
Commit message
Commit date
component-registry.tsx
feat: FoxInput 추가
08-13
component-view.tsx
feat: 디자인 시스템 테스트 하니스를 @fox/dev-test로 이관
08-11
dev-test-page.tsx
docs: @fox 전반의 주석 축약
08-12
dev-test.module.scss
feat: FoxInput 추가
08-13
index.ts
feat: 디자인 시스템 테스트 하니스를 @fox/dev-test로 이관
08-11
read-css-tokens.ts
docs: @fox 전반의 주석 축약
08-12
theme-switch.tsx
docs: @fox 전반의 주석 축약
08-12
token-view.tsx
docs: @fox 전반의 주석 축약
08-12
use-css-tokens.ts
docs: @fox 전반의 주석 축약
08-12
ldu0009 08-13 f7f3037 feat: FoxInput 추가 UNIX
Raw Open in browser Change history
"use client"; import { useState, type ReactNode } from "react"; import { FoxButton } from "../core/components/fox-button"; import { FoxTextButton } from "../core/components/fox-text-button"; import { FoxLinkButton } from "../core/components/fox-link-button"; import { FoxIconButton } from "../core/components/fox-icon-button"; import { FoxButtonGroup } from "../core/components/fox-button-group"; import { FoxButtonPanel } from "../core/components/fox-button-panel"; import { FoxInput, type FoxInputState } from "../core/components/fox-input"; import styles from "./dev-test.module.scss"; export interface ComponentExample { /** 해시 링크에 쓰이는 kebab-case 식별자. */ id: string; name: string; description?: string; /** 눈으로 비교해야 하는 조합을 빠짐없이 넣는다 — 예제가 곧 회귀 확인 수단이다. */ variants: { label: string; node: ReactNode }[]; } // 자리 확인용 도형. `currentColor`로 그려야 계열별 색이 입혀진다. function DemoIcon() { return ( <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true"> <path d="M6 3l5 5-5 5" /> </svg> ); } function DemoStar() { return ( <svg viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"> <path d="M8 1.6l1.9 4 4.4.6-3.2 3 .8 4.3L8 11.5l-3.9 2 .8-4.3-3.2-3 4.4-.6z" /> </svg> ); } // overlay 계열은 어두운 반투명 배경이라 밝은 바탕 위에서는 대비를 볼 수 없다. function OnPhoto({ children }: { children: ReactNode }) { return ( <span style={{ display: "inline-flex", gap: "0.8rem", padding: "0.8rem", borderRadius: "0.8rem", backgroundImage: "url(https://picsum.photos/seed/picsum/200/300)", backgroundSize: "cover", }} > {children} </span> ); } // 입력 예제는 폭이 있어야 시안(360px)처럼 보인다. function InputStage({ children }: { children: ReactNode }) { return <div className={styles.demoField}>{children}</div>; } // 지우기 버튼과 자동 상태 판정을 실제로 눌러 보려면 예제가 값을 들고 있어야 한다. function InputLiveDemo() { const [text, setText] = useState("지우기를 눌러보세요"); return ( <> <InputStage> <FoxInput size="md" label="자동 상태" placeholder="입력하면 글자가 진해집니다" message="포커스하면 도움말이 파랗게 바뀝니다" value={text} onChange={setText} clearable /> </InputStage> <InputStage> <FoxInput size="md" label="오류 (invalid)" placeholder="내용을 입력하세요." message="필수 항목입니다" invalid /> </InputStage> </> ); } // active는 호출부가 소유하므로 켜고 끄는 걸 보려면 예제가 상태를 들고 있어야 한다. function IconToggleDemo() { const [on, setOn] = useState(false); return ( <> <FoxIconButton size="md" type="default" label={on ? "즐겨찾기 해제" : "즐겨찾기"} icon={<DemoStar />} active={on} onAction={() => setOn((prev) => !prev)} /> <FoxIconButton size="md" type="ghost" label={on ? "즐겨찾기 해제" : "즐겨찾기"} icon={<DemoStar />} active={on} onAction={() => setOn((prev) => !prev)} /> <OnPhoto> <FoxIconButton size="md" type="overlay" label={on ? "즐겨찾기 해제" : "즐겨찾기"} icon={<DemoStar />} active={on} onAction={() => setOn((prev) => !prev)} /> </OnPhoto> </> ); } /** 컴포넌트를 만들 때마다 항목 하나를 추가한다 — 다른 파일은 고치지 않는다. */ export const COMPONENT_EXAMPLES: ComponentExample[] = [ { id: "fox-button", name: "FoxButton", description: "Figma 시안(btn, 컴포넌트 세트 106:1606)을 토큰만으로 옮겼습니다. 너비는 내용에 따라 가변이고 높이만 고정입니다. hover·pressed는 마우스를 올리거나 눌러 확인하세요. error 계열은 시안 미정이라 비어 있습니다.", variants: [ { label: "계열 (type) — hover·pressed 확인 가능", node: ( <> <FoxButton size="md" label="default" /> <FoxButton size="md" type="primary" label="primary" /> <FoxButton size="md" type="secondary" label="secondary" /> <FoxButton size="md" type="error" label="error (시안 미정)" /> </> ), }, { label: "크기 (size)", node: ( <> <FoxButton size="lg" label="lg" /> <FoxButton size="md" label="md" /> <FoxButton size="sm" label="sm" /> <FoxButton size="xsm" label="xsm" /> </> ), }, { label: "아이콘", node: ( <> <FoxButton size="md" label="leading" leadingIcon={<DemoIcon />} /> <FoxButton size="md" label="trailing" trailingIcon={<DemoIcon />} /> <FoxButton size="md" label="both" leadingIcon={<DemoIcon />} trailingIcon={<DemoIcon />} /> </> ), }, { label: "상태 — disabled / loading (둘 다 클릭이 막힌다)", node: ( <> <FoxButton size="md" label="normal" onAction={() => undefined} /> <FoxButton size="md" label="disabled" disabled /> <FoxButton size="md" label="loading" loading /> <FoxButton size="md" label="loading + icon" loading leadingIcon={<DemoIcon />} /> </> ), }, { label: "fullWidth", node: <FoxButton size="md" label="fullWidth" fullWidth />, }, { label: "htmlType — 폼 안에서의 역할", node: ( <> <FoxButton size="md" label="button (기본)" /> <FoxButton size="md" label="submit" htmlType="submit" /> <FoxButton size="md" label="reset" htmlType="reset" /> </> ), }, { label: "hidden — 렌더되지 않는다(아래는 비어 있는 것이 정상)", node: <FoxButton size="md" label="hidden" hidden />, }, ], }, { id: "fox-text-button", name: "FoxTextButton", description: "Figma 시안(btn-txt, 컴포넌트 세트 106:2468)을 토큰만으로 옮겼습니다. 높이를 고정하지 않고 패딩과 line-height로만 크기를 잡습니다. lg만 상하 패딩과 간격이 한 단계 넓습니다.", variants: [ { label: "계열 (type) — primary는 굵기가 한 단계 더 굵다", node: ( <> <FoxTextButton size="md" type="primary" label="primary" /> <FoxTextButton size="md" type="secondary" label="secondary" /> </> ), }, { label: "크기 (size)", node: ( <> <FoxTextButton size="lg" label="lg" /> <FoxTextButton size="md" label="md" /> <FoxTextButton size="sm" label="sm" /> <FoxTextButton size="xsm" label="xsm" /> </> ), }, { label: "아이콘", node: ( <> <FoxTextButton size="md" label="leading" leadingIcon={<DemoIcon />} /> <FoxTextButton size="md" label="trailing" trailingIcon={<DemoIcon />} /> <FoxTextButton size="md" label="both" leadingIcon={<DemoIcon />} trailingIcon={<DemoIcon />} /> </> ), }, { label: "상태 — disabled", node: ( <> <FoxTextButton size="md" label="normal" onAction={() => undefined} /> <FoxTextButton size="md" label="disabled" disabled /> <FoxTextButton size="md" type="secondary" label="disabled (secondary)" disabled /> </> ), }, ], }, { id: "fox-link-button", name: "FoxLinkButton", description: "Figma 시안(btn-lnk, 컴포넌트 세트 107:207)을 토큰만으로 옮겼습니다. href를 주면 <a>, 없으면 <button>으로 렌더합니다. 방문 색은 브라우저가 실제 방문 기록으로만 켜므로 아래에서 링크를 한 번 눌러 돌아오면 색이 바뀝니다. tertiary만 평소에 밑줄이 없고 hover에서만 생깁니다.", variants: [ { label: "계열 (type) — hover하면 tertiary에 밑줄이 생긴다", node: ( <> <FoxLinkButton size="md" type="primary" label="primary" href="https://example.com" /> <FoxLinkButton size="md" type="secondary" label="secondary" href="https://example.com" /> <FoxLinkButton size="md" type="tertiary" label="tertiary" href="https://example.com" /> </> ), }, { label: "크기 (size)", node: ( <> <FoxLinkButton size="lg" label="lg" href="https://example.com" /> <FoxLinkButton size="md" label="md" href="https://example.com" /> <FoxLinkButton size="sm" label="sm" href="https://example.com" /> </> ), }, { label: "아이콘 — 라벨 뒤에만 놓인다", node: ( <> <FoxLinkButton size="lg" label="lg" href="https://example.com" icon={<DemoIcon />} /> <FoxLinkButton size="md" label="md" href="https://example.com" icon={<DemoIcon />} /> <FoxLinkButton size="sm" label="sm" href="https://example.com" icon={<DemoIcon />} /> </> ), }, { label: "href 없음 — <button>으로 렌더된다(방문 색이 켜지지 않는다)", node: ( <> <FoxLinkButton size="md" label="onAction만" onAction={() => undefined} /> <FoxLinkButton size="md" type="tertiary" label="tertiary" onAction={() => undefined} /> </> ), }, { label: "상태 — disabled (앵커는 href가 빠져 포커스·클릭이 함께 사라진다)", node: ( <> <FoxLinkButton size="md" label="disabled (a)" href="https://example.com" disabled /> <FoxLinkButton size="md" label="disabled (button)" disabled /> </> ), }, ], }, { id: "fox-icon-button", name: "FoxIconButton", description: "Figma 시안(btn-ico 400:6312 + btn-ico-toggle 404:5247)을 토큰만으로 옮겼습니다. 두 시안이 계열·크기·모양이 같고 켜짐 상태만 달라 한 컴포넌트로 합쳤습니다 — active를 주면 토글(aria-pressed)이 됩니다. ghost는 테두리가 없는 대신 아이콘이 한 단계 큽니다.", variants: [ { label: "계열 (type) — overlay는 사진 위에서 봐야 한다", node: ( <> <FoxIconButton size="md" type="default" label="default" icon={<DemoIcon />} /> <FoxIconButton size="md" type="ghost" label="ghost" icon={<DemoIcon />} /> <OnPhoto> <FoxIconButton size="md" type="overlay" label="overlay" icon={<DemoIcon />} /> </OnPhoto> </> ), }, { label: "크기 (size) — default 계열", node: ( <> <FoxIconButton size="lg" label="lg" icon={<DemoIcon />} /> <FoxIconButton size="md" label="md" icon={<DemoIcon />} /> <FoxIconButton size="sm" label="sm" icon={<DemoIcon />} /> <FoxIconButton size="xsm" label="xsm" icon={<DemoIcon />} /> </> ), }, { label: "크기 (size) — ghost는 같은 상자에 아이콘만 한 단계 크다", node: ( <> <FoxIconButton size="lg" type="ghost" label="lg" icon={<DemoIcon />} /> <FoxIconButton size="md" type="ghost" label="md" icon={<DemoIcon />} /> <FoxIconButton size="sm" type="ghost" label="sm" icon={<DemoIcon />} /> <FoxIconButton size="xsm" type="ghost" label="xsm" icon={<DemoIcon />} /> </> ), }, { label: "토글 — 눌러서 켜고 끈다 (active)", node: <IconToggleDemo />, }, { label: "토글 켜짐 고정 — 계열별 색 비교", node: ( <> <FoxIconButton size="md" type="default" label="on" icon={<DemoStar />} active /> <FoxIconButton size="md" type="ghost" label="on" icon={<DemoStar />} active /> <OnPhoto> <FoxIconButton size="md" type="overlay" label="on" icon={<DemoStar />} active /> </OnPhoto> </> ), }, { label: "상태 — disabled (켜짐보다 우선한다)", node: ( <> <FoxIconButton size="md" label="disabled" icon={<DemoIcon />} disabled /> <FoxIconButton size="md" type="ghost" label="disabled" icon={<DemoIcon />} disabled /> <FoxIconButton size="md" type="overlay" label="disabled" icon={<DemoIcon />} disabled /> <FoxIconButton size="md" label="active + disabled" icon={<DemoStar />} active disabled /> </> ), }, ], }, { id: "fox-button-group", name: "FoxButtonGroup", description: "간격만 Figma 시안(btn-group, 컴포넌트 세트 398:4854)에서 가져왔습니다 — 크기·방향과 무관하게 gap-2(4px) 하나뿐이라 그룹에는 크기별 스타일이 없습니다. 크기는 그룹이 단일 진실 공급원이라 자식이 저마다 다른 size를 줘도 그룹 값으로 통일됩니다. 세로일 때는 자식이 항상 폭을 채웁니다. 블록 레벨 flex라 부모 폭을 채웁니다.", variants: [ { label: "가로 · 정렬 (align) — 회색 테두리가 그룹 영역", node: ( <div className={styles.demoStack}> {(["start", "center", "end"] as const).map((align) => ( <div key={align} className={styles.demoOutline}> <FoxButtonGroup size="md" align={align}> <FoxButton size="md" label={align} /> <FoxButton size="md" type="primary" label="확인" /> </FoxButtonGroup> </div> ))} </div> ), }, { label: "크기 (size) — 자식이 준 size는 무시된다", node: ( <div className={styles.demoStack}> {(["lg", "md", "sm", "xsm"] as const).map((size) => ( <FoxButtonGroup key={size} size={size}> {/* 일부러 어긋난 size를 준다 — 전부 그룹 값으로 통일되어야 정상 */} <FoxButton size="lg" label={size} /> <FoxButton size="xsm" type="primary" label="확인" /> <FoxIconButton size="lg" label="공유" icon={<DemoIcon />} /> </FoxButtonGroup> ))} </div> ), }, { label: "세로 — 자식이 항상 폭을 채운다", node: ( <div className={styles.demoOutline}> <FoxButtonGroup size="md" orientation="vertical"> <FoxButton size="md" label="취소" /> <FoxButton size="md" type="primary" label="확인" /> </FoxButtonGroup> </div> ), }, { label: "wrap — 폭이 모자라면 다음 줄로 넘어간다", node: ( <div className={styles.demoStack}> <div className={styles.demoNarrow}> <FoxButtonGroup size="md" wrap> {["첫째", "둘째", "셋째", "넷째", "다섯째"].map((label) => ( <FoxButton key={label} size="md" label={label} /> ))} </FoxButtonGroup> </div> <div className={styles.demoNarrow}> <FoxButtonGroup size="md"> {["첫째", "둘째", "셋째", "넷째", "다섯째"].map((label) => ( <FoxButton key={label} size="md" label={label} /> ))} </FoxButtonGroup> </div> </div> ), }, { label: "버튼이 아닌 자식 — 호스트 요소는 건드리지 않는다", node: ( <FoxButtonGroup size="sm" align="center"> <FoxButton size="sm" label="이전" /> <span className={styles.demoText}>1 / 10</span> <FoxButton size="sm" label="다음" /> </FoxButtonGroup> ), }, { label: "프래그먼트로 감싼 자식도 덮어쓴다", node: ( <FoxButtonGroup size="xsm"> <> <FoxButton size="lg" label="프래그먼트 안" /> <FoxButton size="lg" type="primary" label="확인" /> </> <FoxButton size="lg" label="프래그먼트 밖" /> </FoxButtonGroup> ), }, ], }, { id: "fox-button-panel", name: "FoxButtonPanel", description: "Figma 시안(btn-page, 컴포넌트 세트 404:5121)을 옮겼습니다. 자식은 버튼 그룹이고, 여럿이면 양끝으로 벌어지고 하나면 가운데로 갑니다 — 자식 수는 CSS :only-child가 판단합니다. 패널 안에서는 그룹 간격이 gap-3(8px)으로 덮어써지고(단독으로 쓰면 gap-2), 버튼에 최소 폭 160px이 붙습니다. 창을 768px 아래로 줄이면 패널도 그룹도 세로로 쌓이고 버튼이 폭을 채웁니다.", variants: [ { label: "그룹 둘 — 양끝 (시안 type=flow)", node: ( <div className={styles.demoOutline}> <FoxButtonPanel offset={false}> <FoxButtonGroup size="lg"> <FoxButton size="lg" label="취소" /> </FoxButtonGroup> <FoxButtonGroup size="lg"> <FoxButton size="lg" type="secondary" label="임시 저장" /> <FoxButton size="lg" type="primary" label="다음" /> </FoxButtonGroup> </FoxButtonPanel> </div> ), }, { label: "그룹 하나 — 가운데 (시안 type=default)", node: ( <div className={styles.demoOutline}> <FoxButtonPanel offset={false}> <FoxButtonGroup size="lg"> <FoxButton size="lg" label="취소" /> <FoxButton size="lg" type="primary" label="확인" /> </FoxButtonGroup> </FoxButtonPanel> </div> ), }, { label: "상단 여백 (offset) — 기본 켜짐, 80px", node: ( <div className={styles.demoOutline}> <FoxButtonPanel> <FoxButtonGroup size="lg"> <FoxButton size="lg" type="primary" label="확인" /> </FoxButtonGroup> </FoxButtonPanel> </div> ), }, { label: "그룹 셋 이상도 양끝 기준으로 벌어진다", node: ( <div className={styles.demoOutline}> <FoxButtonPanel offset={false}> <FoxButtonGroup size="lg"> <FoxButton size="lg" label="삭제" /> </FoxButtonGroup> <FoxButtonGroup size="lg"> <FoxButton size="lg" label="미리보기" /> </FoxButtonGroup> <FoxButtonGroup size="lg"> <FoxButton size="lg" type="primary" label="저장" /> </FoxButtonGroup> </FoxButtonPanel> </div> ), }, ], }, { id: "fox-input", name: "FoxInput", description: "Figma 시안(input, 컴포넌트 세트 135:621)을 옮겼습니다. 네이티브 <input>을 감싸므로 시안에 없는 password·email·number 등 모든 타입을 그대로 받습니다. 상태는 기본적으로 브라우저가 판단하고(포커스·값·비활성·aria-invalid), state를 주면 그 모양으로 고정됩니다. 라벨은 입력 크기와 무관하게 항상 같은 크기입니다.", variants: [ { label: "직접 조작 — 입력·포커스·지우기", node: <InputLiveDemo />, }, { label: "크기 (size) — 상자·글자·아이콘이 함께 줄고 라벨만 고정", node: ( <> {(["lg", "md", "sm"] as const).map((size) => ( <InputStage key={size}> <FoxInput size={size} label={`레이블 (${size})`} placeholder="내용을 입력하세요." /> </InputStage> ))} </> ), }, { label: "상태 고정 (state) — 시안 6종을 그대로 비교", node: ( <> {( ["default", "focused", "completed", "error", "disabled", "view"] as FoxInputState[] ).map((state) => ( <InputStage key={state}> <FoxInput size="md" state={state} label={state} placeholder="내용을 입력하세요." message="힌트 메시지" /> </InputStage> ))} </> ), }, { label: "타입 — 시안에 없는 것도 네이티브 그대로 동작한다", node: ( <> {(["text", "date", "password", "email", "number", "time"] as const).map((type) => ( <InputStage key={type}> <FoxInput size="md" type={type} label={type} placeholder="내용을 입력하세요." /> </InputStage> ))} </> ), }, { label: "아이콘 · 헬퍼 아이콘 슬롯", node: ( <> <InputStage> <FoxInput size="md" label="오른쪽 아이콘" placeholder="내용을 입력하세요." icon={<DemoIcon />} /> </InputStage> <InputStage> <FoxInput size="md" label="헬퍼 아이콘" placeholder="내용을 입력하세요." message="아이콘은 호출부가 넣습니다" messageIcon={<DemoIcon />} /> </InputStage> </> ), }, { label: "비활성 · 읽기 전용 — 네이티브 속성으로 자동 판정", node: ( <> <InputStage> <FoxInput size="md" label="disabled" defaultValue="수정할 수 없음" disabled /> </InputStage> <InputStage> <FoxInput size="md" label="readOnly" defaultValue="읽기 전용" readOnly /> </InputStage> </> ), }, { label: "라벨·헬퍼 없이도 쓸 수 있다", node: ( <InputStage> <FoxInput size="md" placeholder="내용을 입력하세요." /> </InputStage> ), }, ], }, ];

          
        
    
    
Copyright Yona authors & © NAVER Corp. & NAVER LABS Supported by NAVER CLOUD PLATFORM

or
Sign in with github login with Google Sign in with Google
Reset password | Sign up