moru-krds-react 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +61 -0
- package/dist/accordion/index.d.ts +63 -0
- package/dist/accordion/index.d.ts.map +1 -0
- package/dist/accordion/index.js +145 -0
- package/dist/accordion/index.js.map +1 -0
- package/dist/alert/index.d.ts +44 -0
- package/dist/alert/index.d.ts.map +1 -0
- package/dist/alert/index.js +66 -0
- package/dist/alert/index.js.map +1 -0
- package/dist/avatar/index.d.ts +102 -0
- package/dist/avatar/index.d.ts.map +1 -0
- package/dist/avatar/index.js +146 -0
- package/dist/avatar/index.js.map +1 -0
- package/dist/badge/index.d.ts +49 -0
- package/dist/badge/index.d.ts.map +1 -0
- package/dist/badge/index.js +41 -0
- package/dist/badge/index.js.map +1 -0
- package/dist/breadcrumb/index.d.ts +43 -0
- package/dist/breadcrumb/index.d.ts.map +1 -0
- package/dist/breadcrumb/index.js +43 -0
- package/dist/breadcrumb/index.js.map +1 -0
- package/dist/button/index.d.ts +78 -0
- package/dist/button/index.d.ts.map +1 -0
- package/dist/button/index.js +83 -0
- package/dist/button/index.js.map +1 -0
- package/dist/checkbox/index.d.ts +97 -0
- package/dist/checkbox/index.d.ts.map +1 -0
- package/dist/checkbox/index.js +110 -0
- package/dist/checkbox/index.js.map +1 -0
- package/dist/combobox/index.d.ts +135 -0
- package/dist/combobox/index.d.ts.map +1 -0
- package/dist/combobox/index.js +416 -0
- package/dist/combobox/index.js.map +1 -0
- package/dist/context-menu/index.d.ts +94 -0
- package/dist/context-menu/index.d.ts.map +1 -0
- package/dist/context-menu/index.js +399 -0
- package/dist/context-menu/index.js.map +1 -0
- package/dist/critical-alert/index.d.ts +39 -0
- package/dist/critical-alert/index.d.ts.map +1 -0
- package/dist/critical-alert/index.js +50 -0
- package/dist/critical-alert/index.js.map +1 -0
- package/dist/data-table/index.d.ts +94 -0
- package/dist/data-table/index.d.ts.map +1 -0
- package/dist/data-table/index.js +128 -0
- package/dist/data-table/index.js.map +1 -0
- package/dist/disclosure/index.d.ts +35 -0
- package/dist/disclosure/index.d.ts.map +1 -0
- package/dist/disclosure/index.js +54 -0
- package/dist/disclosure/index.js.map +1 -0
- package/dist/drawer/index.d.ts +106 -0
- package/dist/drawer/index.d.ts.map +1 -0
- package/dist/drawer/index.js +352 -0
- package/dist/drawer/index.js.map +1 -0
- package/dist/empty-state/index.d.ts +86 -0
- package/dist/empty-state/index.d.ts.map +1 -0
- package/dist/empty-state/index.js +138 -0
- package/dist/empty-state/index.js.map +1 -0
- package/dist/env.d.js +1 -0
- package/dist/env.d.js.map +1 -0
- package/dist/field/index.d.ts +149 -0
- package/dist/field/index.d.ts.map +1 -0
- package/dist/field/index.js +121 -0
- package/dist/field/index.js.map +1 -0
- package/dist/file-upload/index.d.ts +140 -0
- package/dist/file-upload/index.d.ts.map +1 -0
- package/dist/file-upload/index.js +249 -0
- package/dist/file-upload/index.js.map +1 -0
- package/dist/footer/index.d.ts +155 -0
- package/dist/footer/index.d.ts.map +1 -0
- package/dist/footer/index.js +186 -0
- package/dist/footer/index.js.map +1 -0
- package/dist/header/index.d.ts +170 -0
- package/dist/header/index.d.ts.map +1 -0
- package/dist/header/index.js +258 -0
- package/dist/header/index.js.map +1 -0
- package/dist/identifier/index.d.ts +38 -0
- package/dist/identifier/index.d.ts.map +1 -0
- package/dist/identifier/index.js +55 -0
- package/dist/identifier/index.js.map +1 -0
- package/dist/in-page-navigation/index.d.ts +71 -0
- package/dist/in-page-navigation/index.d.ts.map +1 -0
- package/dist/in-page-navigation/index.js +137 -0
- package/dist/in-page-navigation/index.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/input-group/index.d.ts +66 -0
- package/dist/input-group/index.d.ts.map +1 -0
- package/dist/input-group/index.js +85 -0
- package/dist/input-group/index.js.map +1 -0
- package/dist/lib/krds.d.ts +45 -0
- package/dist/lib/krds.d.ts.map +1 -0
- package/dist/lib/krds.js +11 -0
- package/dist/lib/krds.js.map +1 -0
- package/dist/lib/link.d.ts +34 -0
- package/dist/lib/link.d.ts.map +1 -0
- package/dist/lib/link.js +8 -0
- package/dist/lib/link.js.map +1 -0
- package/dist/main-menu/index.d.ts +123 -0
- package/dist/main-menu/index.d.ts.map +1 -0
- package/dist/main-menu/index.js +613 -0
- package/dist/main-menu/index.js.map +1 -0
- package/dist/masthead/index.d.ts +25 -0
- package/dist/masthead/index.d.ts.map +1 -0
- package/dist/masthead/index.js +21 -0
- package/dist/masthead/index.js.map +1 -0
- package/dist/modal/index.d.ts +105 -0
- package/dist/modal/index.d.ts.map +1 -0
- package/dist/modal/index.js +285 -0
- package/dist/modal/index.js.map +1 -0
- package/dist/pagination/index.d.ts +58 -0
- package/dist/pagination/index.d.ts.map +1 -0
- package/dist/pagination/index.js +82 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/progress/index.d.ts +58 -0
- package/dist/progress/index.d.ts.map +1 -0
- package/dist/progress/index.js +125 -0
- package/dist/progress/index.js.map +1 -0
- package/dist/radio/index.d.ts +111 -0
- package/dist/radio/index.d.ts.map +1 -0
- package/dist/radio/index.js +136 -0
- package/dist/radio/index.js.map +1 -0
- package/dist/resize/index.d.ts +118 -0
- package/dist/resize/index.d.ts.map +1 -0
- package/dist/resize/index.js +342 -0
- package/dist/resize/index.js.map +1 -0
- package/dist/scroll-area/index.d.ts +40 -0
- package/dist/scroll-area/index.d.ts.map +1 -0
- package/dist/scroll-area/index.js +52 -0
- package/dist/scroll-area/index.js.map +1 -0
- package/dist/select/index.d.ts +67 -0
- package/dist/select/index.d.ts.map +1 -0
- package/dist/select/index.js +61 -0
- package/dist/select/index.js.map +1 -0
- package/dist/separator/index.d.ts +47 -0
- package/dist/separator/index.d.ts.map +1 -0
- package/dist/separator/index.js +61 -0
- package/dist/separator/index.js.map +1 -0
- package/dist/side-navigation/index.d.ts +78 -0
- package/dist/side-navigation/index.d.ts.map +1 -0
- package/dist/side-navigation/index.js +188 -0
- package/dist/side-navigation/index.js.map +1 -0
- package/dist/skeleton/index.d.ts +76 -0
- package/dist/skeleton/index.d.ts.map +1 -0
- package/dist/skeleton/index.js +78 -0
- package/dist/skeleton/index.js.map +1 -0
- package/dist/skip-link/index.d.ts +46 -0
- package/dist/skip-link/index.d.ts.map +1 -0
- package/dist/skip-link/index.js +37 -0
- package/dist/skip-link/index.js.map +1 -0
- package/dist/spinner/index.d.ts +58 -0
- package/dist/spinner/index.d.ts.map +1 -0
- package/dist/spinner/index.js +36 -0
- package/dist/spinner/index.js.map +1 -0
- package/dist/step-indicator/index.d.ts +41 -0
- package/dist/step-indicator/index.d.ts.map +1 -0
- package/dist/step-indicator/index.js +63 -0
- package/dist/step-indicator/index.js.map +1 -0
- package/dist/structured-list/index.d.ts +150 -0
- package/dist/structured-list/index.d.ts.map +1 -0
- package/dist/structured-list/index.js +194 -0
- package/dist/structured-list/index.js.map +1 -0
- package/dist/tab/index.d.ts +108 -0
- package/dist/tab/index.d.ts.map +1 -0
- package/dist/tab/index.js +198 -0
- package/dist/tab/index.js.map +1 -0
- package/dist/table/index.d.ts +75 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +91 -0
- package/dist/table/index.js.map +1 -0
- package/dist/tag/index.d.ts +71 -0
- package/dist/tag/index.d.ts.map +1 -0
- package/dist/tag/index.js +45 -0
- package/dist/tag/index.js.map +1 -0
- package/dist/text-input/index.d.ts +69 -0
- package/dist/text-input/index.d.ts.map +1 -0
- package/dist/text-input/index.js +70 -0
- package/dist/text-input/index.js.map +1 -0
- package/dist/text-list/index.d.ts +71 -0
- package/dist/text-list/index.d.ts.map +1 -0
- package/dist/text-list/index.js +79 -0
- package/dist/text-list/index.js.map +1 -0
- package/dist/textarea/index.d.ts +29 -0
- package/dist/textarea/index.d.ts.map +1 -0
- package/dist/textarea/index.js +69 -0
- package/dist/textarea/index.js.map +1 -0
- package/dist/toast/index.d.ts +76 -0
- package/dist/toast/index.d.ts.map +1 -0
- package/dist/toast/index.js +183 -0
- package/dist/toast/index.js.map +1 -0
- package/dist/tooltip/index.d.ts +84 -0
- package/dist/tooltip/index.d.ts.map +1 -0
- package/dist/tooltip/index.js +329 -0
- package/dist/tooltip/index.js.map +1 -0
- package/package.json +57 -0
- package/src/accordion/index.tsx +251 -0
- package/src/alert/index.tsx +111 -0
- package/src/avatar/index.tsx +272 -0
- package/src/badge/index.tsx +91 -0
- package/src/breadcrumb/index.tsx +125 -0
- package/src/button/index.tsx +190 -0
- package/src/checkbox/index.tsx +247 -0
- package/src/combobox/index.tsx +700 -0
- package/src/context-menu/index.tsx +623 -0
- package/src/critical-alert/index.tsx +102 -0
- package/src/data-table/index.tsx +289 -0
- package/src/disclosure/index.tsx +87 -0
- package/src/drawer/index.tsx +516 -0
- package/src/empty-state/index.tsx +256 -0
- package/src/env.d.ts +10 -0
- package/src/field/index.tsx +307 -0
- package/src/file-upload/index.tsx +492 -0
- package/src/footer/index.tsx +393 -0
- package/src/header/index.tsx +498 -0
- package/src/identifier/index.tsx +96 -0
- package/src/in-page-navigation/index.tsx +258 -0
- package/src/index.ts +72 -0
- package/src/input-group/index.tsx +197 -0
- package/src/lib/krds.ts +75 -0
- package/src/lib/link.tsx +35 -0
- package/src/main-menu/index.tsx +912 -0
- package/src/masthead/index.tsx +50 -0
- package/src/modal/index.tsx +457 -0
- package/src/pagination/index.tsx +198 -0
- package/src/progress/index.tsx +198 -0
- package/src/radio/index.tsx +283 -0
- package/src/resize/index.tsx +541 -0
- package/src/scroll-area/index.tsx +96 -0
- package/src/select/index.tsx +136 -0
- package/src/separator/index.tsx +107 -0
- package/src/side-navigation/index.tsx +336 -0
- package/src/skeleton/index.tsx +172 -0
- package/src/skip-link/index.tsx +89 -0
- package/src/spinner/index.tsx +98 -0
- package/src/step-indicator/index.tsx +122 -0
- package/src/structured-list/index.tsx +395 -0
- package/src/tab/index.tsx +348 -0
- package/src/table/index.tsx +186 -0
- package/src/tag/index.tsx +127 -0
- package/src/text-input/index.tsx +156 -0
- package/src/text-list/index.tsx +177 -0
- package/src/textarea/index.tsx +113 -0
- package/src/toast/index.tsx +311 -0
- package/src/tooltip/index.tsx +496 -0
- package/styles/krds-tailwind.css +335 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/identifier/index.tsx"],"sourcesContent":["import type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { cx } from '../lib/krds';\n\n/**\n * 운영기관 식별자 — KRDS `krds-identifier`.\n *\n * 「이 누리집은 [기관]에서 운영하는 누리집입니다.」 안내. 푸터 안 **가장 마지막 구획**에 둔다.\n * 여기 들어가는 로고는 서비스 로고가 아니라 **운영 주체 기관**의 로고다(서비스 로고는 `f-logo`).\n * 문구 형식과 요소 배치는 규격이 변형을 금지한다 — `children` 은 번역을 넣는 자리다.\n * 푸터 골격을 조립할 때는 `ui/footer` 의 `FooterIdentifier` 를 쓴다.\n *\n * @example\n * <Identifier org=\"○○기관\" logoSrc=\"/site/logo-mark.svg\" />\n * <Identifier org=\"○○기관\" variant=\"text\" dark />\n *\n * 자세히: docs/krds/09-부품-노트.md#운영기관식별자\n */\n\n/**\n * 표현 방식. `card` 는 흰 면 + 둥근 상자(기본), `text` 는 한 줄짜리 킷 전용 변형이다.\n */\nexport type IdentifierVariant = 'card' | 'text';\n\nexport type IdentifierProps = Omit<ComponentPropsWithRef<'section'>, 'children'> & {\n /** 운영기관명. 안내 문구와 로고 대체 텍스트에 함께 쓰인다. */\n org: string;\n /** 운영기관 로고 이미지 경로. 없으면 `.logo` 를 아예 그리지 않는다(킷 마크가 찍히는 것을 막는다). */\n logoSrc?: string;\n /** 표현 방식. 기본 `card`. */\n variant?: IdentifierVariant;\n /** 어두운 바탕용. 킷에 클래스가 없어 KRDS 반전 토큰으로 그린다. */\n dark?: boolean;\n /** 안내 문구. 다국어 번역을 넣을 때만 쓰고 형식은 바꾸지 않는다. */\n children?: ReactNode;\n /** 구획 이름(`aria-label`). `<section>` 은 이름이 있어야 region 으로 노출된다. */\n label?: string;\n /** 가이드가 허용하는 두 요소. 기본은 `section`. */\n as?: 'section' | 'article';\n className?: string;\n};\n\nexport function Identifier({\n org,\n logoSrc,\n variant = 'card',\n dark,\n label = '운영기관 식별자',\n as: Tag = 'section',\n className,\n children,\n ...rest\n}: IdentifierProps) {\n // 가이드 형식: `(운영기관 로고) 이 누리집은 [운영기관명]에서 운영하는 누리집입니다.`\n const text = children ?? `이 누리집은 ${org}에서 운영하는 누리집입니다.`;\n\n const logo = logoSrc ? (\n <span\n className={cx(\n 'logo',\n // 킷의 KRDS 마크 배경과 고정 폭을 지운다. 높이는 킷 값을 그대로 쓴다.\n 'inline-flex items-center bg-none w-auto',\n // text 변형에는 킷 `.logo` 규칙이 닿지 않아 높이가 없다 — 본문 소자 줄에 맞춘다.\n variant === 'text' && 'h-5',\n )}\n >\n {/* 이름은 옆의 sr-only 가 준다. alt 까지 채우면 두 번 읽힌다. */}\n <img src={logoSrc} alt=\"\" className=\"h-full w-auto\" />\n <span className=\"sr-only\">{org}</span>\n </span>\n ) : null;\n\n const cls =\n variant === 'text'\n ? cx(\n 'krds-identifier-txt',\n // 킷 규칙이 `#krds-footer .f-btm` 안에서만 걸린다 — 밖에서도 같은 줄로 서게 보충한다.\n // 킷의 `white-space:nowrap` 은 옮기지 않는다(긴 기관명이 좁은 화면에서 넘친다).\n 'flex items-center gap-2 text-sm',\n dark && 'text-fg-inverse',\n className,\n )\n : cx(\n 'krds-identifier',\n // 킷의 흰 면(`--krds-light-color-surface-white`)을 반전 토큰으로 덮는다.\n dark && 'bg-surface-inverse text-fg-inverse',\n className,\n );\n\n return (\n // rest 를 뒤에 두어 화면 쪽에서 aria-label 을 덮어쓸 수 있게 한다.\n <Tag aria-label={label} className={cls} {...rest}>\n {logo}\n {variant === 'text' ? <span>{text}</span> : <span className=\"ban-txt\">{text}</span>}\n </Tag>\n );\n}\n"],"mappings":"AAwDI,SAUE,KAVF;AAvDJ,SAAS,UAAU;AAwCZ,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA,QAAQ;AAAA,EACR,IAAI,MAAM;AAAA,EACV;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAElB,QAAM,OAAO,YAAY,mCAAU,GAAG;AAEtC,QAAM,OAAO,UACX;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA;AAAA,QAEA;AAAA;AAAA,QAEA,YAAY,UAAU;AAAA,MACxB;AAAA,MAGA;AAAA,4BAAC,SAAI,KAAK,SAAS,KAAI,IAAG,WAAU,iBAAgB;AAAA,QACpD,oBAAC,UAAK,WAAU,WAAW,eAAI;AAAA;AAAA;AAAA,EACjC,IACE;AAEJ,QAAM,MACJ,YAAY,SACR;AAAA,IACE;AAAA;AAAA;AAAA,IAGA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EACF,IACA;AAAA,IACE;AAAA;AAAA,IAEA,QAAQ;AAAA,IACR;AAAA,EACF;AAEN;AAAA;AAAA,IAEE,qBAAC,OAAI,cAAY,OAAO,WAAW,KAAM,GAAG,MACzC;AAAA;AAAA,MACA,YAAY,SAAS,oBAAC,UAAM,gBAAK,IAAU,oBAAC,UAAK,WAAU,WAAW,gBAAK;AAAA,OAC9E;AAAA;AAEJ;","names":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type ComponentPropsWithRef, type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 콘텐츠 내 탐색(목차) — KRDS `krds-in-page-navigation-area`.
|
|
4
|
+
*
|
|
5
|
+
* 긴 상세 화면의 섹션 목차. 본문 섹션에 `id` 만 주면 스크롤 감시·부드러운 이동·초점 이동을
|
|
6
|
+
* 이 부품이 한다(킷 JS 를 싣지 않는다). 항목 이름은 **연결된 섹션 제목과 같은 문구**로 쓴다.
|
|
7
|
+
* 컨테이너에 {@link InPageNavigationLayout} 을 붙여야 목차 자리가 열린다 —
|
|
8
|
+
* 목차에만 붙이면 고정된 목차가 본문 위를 덮는다.
|
|
9
|
+
* 마크업 자리는 제목과 본문 **사이**다. 서비스 정보구조 탐색은 이것이 아니라 `ui/side-navigation` 이다.
|
|
10
|
+
* `action` 에 넣은 버튼은 **모바일에서 킷이 숨기므로** 유일한 실행 수단을 여기 두지 마라.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <InPageNavigationLayout id="container">
|
|
14
|
+
* <div className="inner">
|
|
15
|
+
* <div className="contents">
|
|
16
|
+
* <InPageNavigation
|
|
17
|
+
* title="장애아동수당"
|
|
18
|
+
* items={[
|
|
19
|
+
* { id: 'sec-overview', label: '서비스 개요' },
|
|
20
|
+
* { id: 'sec-detail', label: '서비스 상세' },
|
|
21
|
+
* ]}
|
|
22
|
+
* />
|
|
23
|
+
* <section id="sec-overview"><h2>서비스 개요</h2>…</section>
|
|
24
|
+
* </div>
|
|
25
|
+
* </div>
|
|
26
|
+
* </InPageNavigationLayout>
|
|
27
|
+
*
|
|
28
|
+
* 자세히: docs/krds/09-부품-노트.md#페이지내탐색
|
|
29
|
+
*/
|
|
30
|
+
/** 목차 한 줄. `id` 는 본문 섹션 요소의 `id`(`#` 없이). */
|
|
31
|
+
export type InPageNavigationItem = {
|
|
32
|
+
/** 이동 대상 섹션의 `id`. `href="#{id}"` 로 나간다. */
|
|
33
|
+
id: string;
|
|
34
|
+
/** 목록에 보일 글자. 연결된 섹션 제목과 같은 문구여야 한다. 길면 두 줄로 접힌다. */
|
|
35
|
+
label: ReactNode;
|
|
36
|
+
};
|
|
37
|
+
export type InPageNavigationProps = Omit<ComponentPropsWithRef<'div'>, 'children' | 'title'> & {
|
|
38
|
+
/** 목차 항목. 빈 배열이면 아무것도 그리지 않는다. 8개 안팎으로 줄인다. */
|
|
39
|
+
items: InPageNavigationItem[];
|
|
40
|
+
/** 머리말 위 작은 글자. 기본 「이 페이지의 구성」. `null` 을 주면 그리지 않는다. */
|
|
41
|
+
caption?: ReactNode;
|
|
42
|
+
/** 머리말 제목. 보통 현재 화면 이름. 제목 태그가 아니라 `<p>` 로 그린다. */
|
|
43
|
+
title?: ReactNode;
|
|
44
|
+
/** `<nav>` 의 접근 이름. 생략하면 `caption` 을 쓴다. */
|
|
45
|
+
navLabel?: string;
|
|
46
|
+
/** 목록 아래 실행 버튼. **모바일에서는 킷이 숨긴다.** */
|
|
47
|
+
action?: ReactNode;
|
|
48
|
+
/** 실행 버튼 아래 보조 문구. */
|
|
49
|
+
info?: ReactNode;
|
|
50
|
+
/** 활성 항목을 밖에서 정한다. 주면 스크롤 감시를 끄고 이 값만 따른다. */
|
|
51
|
+
activeId?: string;
|
|
52
|
+
/** 이동한 뒤 섹션 위에 남길 여백(px). 안 주면 마스트헤드 + 헤더 높이를 잰다. */
|
|
53
|
+
offsetTop?: number;
|
|
54
|
+
};
|
|
55
|
+
export declare function InPageNavigation({ items, caption, title, navLabel, action, info, activeId, offsetTop, className, ...rest }: InPageNavigationProps): import("react").JSX.Element | null;
|
|
56
|
+
/**
|
|
57
|
+
* 목차가 있는 화면의 컨테이너 — 킷 `krds-in-page-navigation-type`.
|
|
58
|
+
*
|
|
59
|
+
* **목차가 아니라 컨테이너에 붙인다** — 이 클래스가 본문 오른쪽에 목차 자리를 파 준다.
|
|
60
|
+
* 안쪽은 `.inner`(LNB 가 같이 있으면 `.inner.in-between`) → `.contents` 순서다.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* <InPageNavigationLayout id="container">
|
|
64
|
+
* <div className="inner in-between">
|
|
65
|
+
* <SideNav … />
|
|
66
|
+
* <div className="contents">…<InPageNavigation … />…</div>
|
|
67
|
+
* </div>
|
|
68
|
+
* </InPageNavigationLayout>
|
|
69
|
+
*/
|
|
70
|
+
export declare function InPageNavigationLayout({ className, children, ...rest }: ComponentPropsWithRef<'div'>): import("react").JSX.Element;
|
|
71
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/in-page-navigation/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,qBAAqB,EAE1B,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,6CAA6C;AAC7C,MAAM,MAAM,oBAAoB,GAAG;IACjC,4CAA4C;IAC5C,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,KAAK,EAAE,SAAS,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG;IAC7F,+CAA+C;IAC/C,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,wDAAwD;IACxD,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,mDAAmD;IACnD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,sBAAsB;IACtB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAkCF,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAqB,EACrB,KAAK,EACL,QAAQ,EACR,MAAM,EACN,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,SAAS,EACT,GAAG,IAAI,EACR,EAAE,qBAAqB,sCAwHvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,qBAAqB,CAAC,KAAK,CAAC,+BAM9B"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import {
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState
|
|
7
|
+
} from "react";
|
|
8
|
+
import { cx } from "../lib/krds.js";
|
|
9
|
+
function headerHeight() {
|
|
10
|
+
const masthead = document.getElementById("krds-masthead")?.clientHeight ?? 0;
|
|
11
|
+
const headerIn = document.querySelector("#krds-header .header-in")?.clientHeight ?? 0;
|
|
12
|
+
return masthead + headerIn;
|
|
13
|
+
}
|
|
14
|
+
function pickActiveId(ids) {
|
|
15
|
+
const found = ids.map((id) => ({ id, el: document.getElementById(id) })).filter((s) => s.el !== null);
|
|
16
|
+
if (found.length === 0) return void 0;
|
|
17
|
+
const y = window.scrollY;
|
|
18
|
+
if (y + window.innerHeight >= document.documentElement.scrollHeight - 1) {
|
|
19
|
+
return found[found.length - 1].id;
|
|
20
|
+
}
|
|
21
|
+
const guard = Math.ceil(window.innerHeight / 5);
|
|
22
|
+
let active = found[0].id;
|
|
23
|
+
for (const { id, el } of found) {
|
|
24
|
+
if (y >= el.getBoundingClientRect().top + y - guard) active = id;
|
|
25
|
+
}
|
|
26
|
+
return active;
|
|
27
|
+
}
|
|
28
|
+
function InPageNavigation({
|
|
29
|
+
items,
|
|
30
|
+
caption = "\uC774 \uD398\uC774\uC9C0\uC758 \uAD6C\uC131",
|
|
31
|
+
title,
|
|
32
|
+
navLabel,
|
|
33
|
+
action,
|
|
34
|
+
info,
|
|
35
|
+
activeId,
|
|
36
|
+
offsetTop,
|
|
37
|
+
className,
|
|
38
|
+
...rest
|
|
39
|
+
}) {
|
|
40
|
+
const [spyId, setSpyId] = useState(void 0);
|
|
41
|
+
const controlled = activeId !== void 0;
|
|
42
|
+
const active = controlled ? activeId : spyId;
|
|
43
|
+
const idKey = items.map((it) => it.id).join(" ");
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (controlled) return;
|
|
46
|
+
const ids = idKey ? idKey.split(" ") : [];
|
|
47
|
+
if (ids.length === 0) return;
|
|
48
|
+
let frame = 0;
|
|
49
|
+
const update = () => {
|
|
50
|
+
frame = 0;
|
|
51
|
+
setSpyId(pickActiveId(ids));
|
|
52
|
+
};
|
|
53
|
+
const schedule = () => {
|
|
54
|
+
if (frame === 0) frame = window.requestAnimationFrame(update);
|
|
55
|
+
};
|
|
56
|
+
update();
|
|
57
|
+
window.addEventListener("scroll", schedule, { passive: true });
|
|
58
|
+
window.addEventListener("resize", schedule);
|
|
59
|
+
return () => {
|
|
60
|
+
if (frame !== 0) window.cancelAnimationFrame(frame);
|
|
61
|
+
window.removeEventListener("scroll", schedule);
|
|
62
|
+
window.removeEventListener("resize", schedule);
|
|
63
|
+
};
|
|
64
|
+
}, [controlled, idKey]);
|
|
65
|
+
const goToSection = useCallback(
|
|
66
|
+
(id) => {
|
|
67
|
+
const target = document.getElementById(id);
|
|
68
|
+
if (!target) return false;
|
|
69
|
+
const gap = offsetTop ?? headerHeight();
|
|
70
|
+
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
71
|
+
window.scrollTo({
|
|
72
|
+
left: 0,
|
|
73
|
+
top: target.getBoundingClientRect().top + window.scrollY - gap,
|
|
74
|
+
behavior: reduced ? "auto" : "smooth"
|
|
75
|
+
});
|
|
76
|
+
const focusTarget = target.querySelector("h1, h2, h3, h4, h5, h6") ?? target;
|
|
77
|
+
focusTarget.setAttribute("tabindex", "-1");
|
|
78
|
+
focusTarget.focus({ preventScroll: true });
|
|
79
|
+
return true;
|
|
80
|
+
},
|
|
81
|
+
[offsetTop]
|
|
82
|
+
);
|
|
83
|
+
const handleClick = (event, id) => {
|
|
84
|
+
if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (goToSection(id)) {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
if (!controlled) setSpyId(id);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
if (items.length === 0) return null;
|
|
93
|
+
const hasHeader = caption != null || title != null;
|
|
94
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("krds-in-page-navigation-area", className), ...rest, children: [
|
|
95
|
+
hasHeader && /* @__PURE__ */ jsxs("div", { className: "in-page-navigation-header", children: [
|
|
96
|
+
caption != null && /* @__PURE__ */ jsx("p", { className: "quick-caption", children: caption }),
|
|
97
|
+
title != null && /* @__PURE__ */ jsx("p", { className: "quick-title", children: title })
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ jsx(
|
|
100
|
+
"nav",
|
|
101
|
+
{
|
|
102
|
+
className: "in-page-navigation-list",
|
|
103
|
+
"aria-label": navLabel ?? (typeof caption === "string" ? caption : "\uC774 \uD398\uC774\uC9C0\uC758 \uAD6C\uC131"),
|
|
104
|
+
children: /* @__PURE__ */ jsx("ul", { children: items.map((item) => {
|
|
105
|
+
const isActive = item.id === active;
|
|
106
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
107
|
+
"a",
|
|
108
|
+
{
|
|
109
|
+
href: `#${item.id}`,
|
|
110
|
+
className: isActive ? "active" : void 0,
|
|
111
|
+
"aria-current": isActive ? "true" : void 0,
|
|
112
|
+
onClick: (event) => handleClick(event, item.id),
|
|
113
|
+
children: item.label
|
|
114
|
+
}
|
|
115
|
+
) }, item.id);
|
|
116
|
+
}) })
|
|
117
|
+
}
|
|
118
|
+
),
|
|
119
|
+
(action != null || info != null) && // 767px 이하에서 킷이 이 블록을 통째로 `display:none` 한다. 유일한 실행 수단을 여기 두지 마라.
|
|
120
|
+
/* @__PURE__ */ jsxs("div", { className: "in-page-navigation-action", children: [
|
|
121
|
+
action,
|
|
122
|
+
info != null && /* @__PURE__ */ jsx("p", { className: "quick-info", children: info })
|
|
123
|
+
] })
|
|
124
|
+
] });
|
|
125
|
+
}
|
|
126
|
+
function InPageNavigationLayout({
|
|
127
|
+
className,
|
|
128
|
+
children,
|
|
129
|
+
...rest
|
|
130
|
+
}) {
|
|
131
|
+
return /* @__PURE__ */ jsx("div", { className: cx("krds-in-page-navigation-type", className), ...rest, children });
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
InPageNavigation,
|
|
135
|
+
InPageNavigationLayout
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/in-page-navigation/index.tsx"],"sourcesContent":["'use client';\n\nimport {\n useCallback,\n useEffect,\n useState,\n type ComponentPropsWithRef,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from 'react';\nimport { cx } from '../lib/krds';\n\n/**\n * 콘텐츠 내 탐색(목차) — KRDS `krds-in-page-navigation-area`.\n *\n * 긴 상세 화면의 섹션 목차. 본문 섹션에 `id` 만 주면 스크롤 감시·부드러운 이동·초점 이동을\n * 이 부품이 한다(킷 JS 를 싣지 않는다). 항목 이름은 **연결된 섹션 제목과 같은 문구**로 쓴다.\n * 컨테이너에 {@link InPageNavigationLayout} 을 붙여야 목차 자리가 열린다 —\n * 목차에만 붙이면 고정된 목차가 본문 위를 덮는다.\n * 마크업 자리는 제목과 본문 **사이**다. 서비스 정보구조 탐색은 이것이 아니라 `ui/side-navigation` 이다.\n * `action` 에 넣은 버튼은 **모바일에서 킷이 숨기므로** 유일한 실행 수단을 여기 두지 마라.\n *\n * @example\n * <InPageNavigationLayout id=\"container\">\n * <div className=\"inner\">\n * <div className=\"contents\">\n * <InPageNavigation\n * title=\"장애아동수당\"\n * items={[\n * { id: 'sec-overview', label: '서비스 개요' },\n * { id: 'sec-detail', label: '서비스 상세' },\n * ]}\n * />\n * <section id=\"sec-overview\"><h2>서비스 개요</h2>…</section>\n * </div>\n * </div>\n * </InPageNavigationLayout>\n *\n * 자세히: docs/krds/09-부품-노트.md#페이지내탐색\n */\n\n/** 목차 한 줄. `id` 는 본문 섹션 요소의 `id`(`#` 없이). */\nexport type InPageNavigationItem = {\n /** 이동 대상 섹션의 `id`. `href=\"#{id}\"` 로 나간다. */\n id: string;\n /** 목록에 보일 글자. 연결된 섹션 제목과 같은 문구여야 한다. 길면 두 줄로 접힌다. */\n label: ReactNode;\n};\n\nexport type InPageNavigationProps = Omit<ComponentPropsWithRef<'div'>, 'children' | 'title'> & {\n /** 목차 항목. 빈 배열이면 아무것도 그리지 않는다. 8개 안팎으로 줄인다. */\n items: InPageNavigationItem[];\n /** 머리말 위 작은 글자. 기본 「이 페이지의 구성」. `null` 을 주면 그리지 않는다. */\n caption?: ReactNode;\n /** 머리말 제목. 보통 현재 화면 이름. 제목 태그가 아니라 `<p>` 로 그린다. */\n title?: ReactNode;\n /** `<nav>` 의 접근 이름. 생략하면 `caption` 을 쓴다. */\n navLabel?: string;\n /** 목록 아래 실행 버튼. **모바일에서는 킷이 숨긴다.** */\n action?: ReactNode;\n /** 실행 버튼 아래 보조 문구. */\n info?: ReactNode;\n /** 활성 항목을 밖에서 정한다. 주면 스크롤 감시를 끄고 이 값만 따른다. */\n activeId?: string;\n /** 이동한 뒤 섹션 위에 남길 여백(px). 안 주면 마스트헤드 + 헤더 높이를 잰다. */\n offsetTop?: number;\n};\n\n/** 고정 헤더 높이. 킷 JS `calculateHeaderHeight()` 와 같은 계산이다. */\nfunction headerHeight(): number {\n const masthead = document.getElementById('krds-masthead')?.clientHeight ?? 0;\n const headerIn =\n document.querySelector<HTMLElement>('#krds-header .header-in')?.clientHeight ?? 0;\n return masthead + headerIn;\n}\n\n/**\n * 지금 보고 있는 섹션을 고른다. 킷 JS `updateActiveSection()` 을 옮기되\n * 문서 기준 좌표로 재고, 섹션 사이 빈틈에서는 **지나온 마지막 섹션**을 고른다.\n */\nfunction pickActiveId(ids: string[]): string | undefined {\n const found = ids\n .map((id) => ({ id, el: document.getElementById(id) }))\n .filter((s): s is { id: string; el: HTMLElement } => s.el !== null);\n if (found.length === 0) return undefined;\n\n const y = window.scrollY;\n // 스크롤이 문서 끝에 닿으면 마지막 항목(짧은 마지막 섹션이 영영 활성화되지 못하는 것을 막는다).\n if (y + window.innerHeight >= document.documentElement.scrollHeight - 1) {\n return found[found.length - 1]!.id;\n }\n\n const guard = Math.ceil(window.innerHeight / 5); // 킷과 같은 「윈도우의 20%」\n let active = found[0]!.id;\n for (const { id, el } of found) {\n if (y >= el.getBoundingClientRect().top + y - guard) active = id;\n }\n return active;\n}\n\nexport function InPageNavigation({\n items,\n caption = '이 페이지의 구성',\n title,\n navLabel,\n action,\n info,\n activeId,\n offsetTop,\n className,\n ...rest\n}: InPageNavigationProps) {\n const [spyId, setSpyId] = useState<string | undefined>(undefined);\n const controlled = activeId !== undefined;\n const active = controlled ? activeId : spyId;\n\n // 항목 id 를 문자열 하나로 굳혀 effect 의존성에 쓴다(배열이 매번 새로 만들어져도 다시 붙지 않게).\n const idKey = items.map((it) => it.id).join(' ');\n\n useEffect(() => {\n if (controlled) return;\n const ids = idKey ? idKey.split(' ') : [];\n if (ids.length === 0) return;\n\n let frame = 0;\n const update = () => {\n frame = 0;\n setSpyId(pickActiveId(ids));\n };\n // 스크롤마다 레이아웃을 재는 계산이라 프레임당 한 번으로 묶는다.\n const schedule = () => {\n if (frame === 0) frame = window.requestAnimationFrame(update);\n };\n\n update();\n window.addEventListener('scroll', schedule, { passive: true });\n window.addEventListener('resize', schedule);\n return () => {\n if (frame !== 0) window.cancelAnimationFrame(frame);\n window.removeEventListener('scroll', schedule);\n window.removeEventListener('resize', schedule);\n };\n }, [controlled, idKey]);\n\n const goToSection = useCallback(\n (id: string): boolean => {\n const target = document.getElementById(id);\n if (!target) return false; // 대상이 없으면 브라우저 기본 앵커 이동에 맡긴다\n\n const gap = offsetTop ?? headerHeight();\n const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n window.scrollTo({\n left: 0,\n top: target.getBoundingClientRect().top + window.scrollY - gap,\n behavior: reduced ? 'auto' : 'smooth',\n });\n\n // 스크롤만 하고 초점을 두면 규격 위반이다(「스크롤과 함께 목적지 섹션으로 Focus 이벤트」).\n // 킷은 `.sec-tit` 을 찾지만 그 클래스는 킷 CSS 에 없다 — 섹션의 첫 제목 태그를 쓴다.\n const focusTarget = target.querySelector<HTMLElement>('h1, h2, h3, h4, h5, h6') ?? target;\n focusTarget.setAttribute('tabindex', '-1');\n focusTarget.focus({ preventScroll: true });\n return true;\n },\n [offsetTop],\n );\n\n const handleClick = (event: ReactMouseEvent<HTMLAnchorElement>, id: string) => {\n // 새 탭·새 창으로 여는 조작은 그대로 둔다(킷 JS 는 이것까지 막는다).\n if (\n event.defaultPrevented ||\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.shiftKey ||\n event.altKey\n ) {\n return;\n }\n if (goToSection(id)) {\n event.preventDefault();\n if (!controlled) setSpyId(id);\n }\n };\n\n if (items.length === 0) return null;\n\n const hasHeader = caption != null || title != null;\n\n return (\n <div className={cx('krds-in-page-navigation-area', className)} {...rest}>\n {hasHeader && (\n <div className=\"in-page-navigation-header\">\n {caption != null && <p className=\"quick-caption\">{caption}</p>}\n {/* 제목 태그가 아니라 `<p>` 다 — 본문 제목 계층을 흐리지 않으려는 킷 샘플 그대로. */}\n {title != null && <p className=\"quick-title\">{title}</p>}\n </div>\n )}\n\n <nav\n className=\"in-page-navigation-list\"\n aria-label={navLabel ?? (typeof caption === 'string' ? caption : '이 페이지의 구성')}\n >\n <ul>\n {items.map((item) => {\n const isActive = item.id === active;\n return (\n <li key={item.id}>\n <a\n href={`#${item.id}`}\n className={isActive ? 'active' : undefined}\n aria-current={isActive ? 'true' : undefined}\n onClick={(event) => handleClick(event, item.id)}\n >\n {item.label}\n </a>\n </li>\n );\n })}\n </ul>\n </nav>\n\n {(action != null || info != null) && (\n // 767px 이하에서 킷이 이 블록을 통째로 `display:none` 한다. 유일한 실행 수단을 여기 두지 마라.\n <div className=\"in-page-navigation-action\">\n {action}\n {info != null && <p className=\"quick-info\">{info}</p>}\n </div>\n )}\n </div>\n );\n}\n\n/**\n * 목차가 있는 화면의 컨테이너 — 킷 `krds-in-page-navigation-type`.\n *\n * **목차가 아니라 컨테이너에 붙인다** — 이 클래스가 본문 오른쪽에 목차 자리를 파 준다.\n * 안쪽은 `.inner`(LNB 가 같이 있으면 `.inner.in-between`) → `.contents` 순서다.\n *\n * @example\n * <InPageNavigationLayout id=\"container\">\n * <div className=\"inner in-between\">\n * <SideNav … />\n * <div className=\"contents\">…<InPageNavigation … />…</div>\n * </div>\n * </InPageNavigationLayout>\n */\nexport function InPageNavigationLayout({\n className,\n children,\n ...rest\n}: ComponentPropsWithRef<'div'>) {\n return (\n <div className={cx('krds-in-page-navigation-type', className)} {...rest}>\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KRDS(디지털정부서비스 UI/UX 가이드라인) React 부품.
|
|
3
|
+
*
|
|
4
|
+
* 두 갈래다.
|
|
5
|
+
* 1. **KRDS 부품** — 킷(`krds-uiux`)의 클래스를 그대로 쓰는 껍데기.
|
|
6
|
+
* 모양·색·크기는 전부 킷에서 오고, 이 층은 마크업 구조와 접근성 속성을 고정한다.
|
|
7
|
+
* 2. **보충 부품** — KRDS 가 다루지 않는 업무시스템(백오피스)용. KRDS 토큰으로 그린다.
|
|
8
|
+
* KRDS 는 국민용 누리집이 대상이라 표 정렬·콤보박스·서랍·토스트 같은 것이 없다.
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
export * from './lib/krds';
|
|
12
|
+
export * from './button';
|
|
13
|
+
export * from './field';
|
|
14
|
+
export * from './text-input';
|
|
15
|
+
export * from './textarea';
|
|
16
|
+
export * from './select';
|
|
17
|
+
export * from './checkbox';
|
|
18
|
+
export * from './radio';
|
|
19
|
+
export * from './file-upload';
|
|
20
|
+
export * from './combobox';
|
|
21
|
+
export * from './input-group';
|
|
22
|
+
export * from './badge';
|
|
23
|
+
export * from './tag';
|
|
24
|
+
export * from './table';
|
|
25
|
+
export * from './data-table';
|
|
26
|
+
export * from './structured-list';
|
|
27
|
+
export * from './text-list';
|
|
28
|
+
export * from './critical-alert';
|
|
29
|
+
export * from './alert';
|
|
30
|
+
export * from './empty-state';
|
|
31
|
+
export * from './skip-link';
|
|
32
|
+
export * from './masthead';
|
|
33
|
+
export * from './header';
|
|
34
|
+
export * from './main-menu';
|
|
35
|
+
export * from './breadcrumb';
|
|
36
|
+
export * from './side-navigation';
|
|
37
|
+
export * from './in-page-navigation';
|
|
38
|
+
export * from './pagination';
|
|
39
|
+
export * from './footer';
|
|
40
|
+
export * from './identifier';
|
|
41
|
+
export * from './accordion';
|
|
42
|
+
export * from './disclosure';
|
|
43
|
+
export * from './tab';
|
|
44
|
+
export * from './modal';
|
|
45
|
+
export * from './drawer';
|
|
46
|
+
export * from './tooltip';
|
|
47
|
+
export * from './context-menu';
|
|
48
|
+
export * from './spinner';
|
|
49
|
+
export * from './step-indicator';
|
|
50
|
+
export * from './progress';
|
|
51
|
+
export * from './skeleton';
|
|
52
|
+
export * from './toast';
|
|
53
|
+
export * from './avatar';
|
|
54
|
+
export * from './separator';
|
|
55
|
+
export * from './scroll-area';
|
|
56
|
+
export * from './resize';
|
|
57
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,UAAU,CAAC;AAGzB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAC9B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAG9B,cAAc,SAAS,CAAC;AACxB,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AACxB,cAAc,eAAe,CAAC;AAG9B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAG7B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,OAAO,CAAC;AACtB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AAGxB,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export * from "./lib/krds.js";
|
|
2
|
+
export * from "./button/index.js";
|
|
3
|
+
export * from "./field/index.js";
|
|
4
|
+
export * from "./text-input/index.js";
|
|
5
|
+
export * from "./textarea/index.js";
|
|
6
|
+
export * from "./select/index.js";
|
|
7
|
+
export * from "./checkbox/index.js";
|
|
8
|
+
export * from "./radio/index.js";
|
|
9
|
+
export * from "./file-upload/index.js";
|
|
10
|
+
export * from "./combobox/index.js";
|
|
11
|
+
export * from "./input-group/index.js";
|
|
12
|
+
export * from "./badge/index.js";
|
|
13
|
+
export * from "./tag/index.js";
|
|
14
|
+
export * from "./table/index.js";
|
|
15
|
+
export * from "./data-table/index.js";
|
|
16
|
+
export * from "./structured-list/index.js";
|
|
17
|
+
export * from "./text-list/index.js";
|
|
18
|
+
export * from "./critical-alert/index.js";
|
|
19
|
+
export * from "./alert/index.js";
|
|
20
|
+
export * from "./empty-state/index.js";
|
|
21
|
+
export * from "./skip-link/index.js";
|
|
22
|
+
export * from "./masthead/index.js";
|
|
23
|
+
export * from "./header/index.js";
|
|
24
|
+
export * from "./main-menu/index.js";
|
|
25
|
+
export * from "./breadcrumb/index.js";
|
|
26
|
+
export * from "./side-navigation/index.js";
|
|
27
|
+
export * from "./in-page-navigation/index.js";
|
|
28
|
+
export * from "./pagination/index.js";
|
|
29
|
+
export * from "./footer/index.js";
|
|
30
|
+
export * from "./identifier/index.js";
|
|
31
|
+
export * from "./accordion/index.js";
|
|
32
|
+
export * from "./disclosure/index.js";
|
|
33
|
+
export * from "./tab/index.js";
|
|
34
|
+
export * from "./modal/index.js";
|
|
35
|
+
export * from "./drawer/index.js";
|
|
36
|
+
export * from "./tooltip/index.js";
|
|
37
|
+
export * from "./context-menu/index.js";
|
|
38
|
+
export * from "./spinner/index.js";
|
|
39
|
+
export * from "./step-indicator/index.js";
|
|
40
|
+
export * from "./progress/index.js";
|
|
41
|
+
export * from "./skeleton/index.js";
|
|
42
|
+
export * from "./toast/index.js";
|
|
43
|
+
export * from "./avatar/index.js";
|
|
44
|
+
export * from "./separator/index.js";
|
|
45
|
+
export * from "./scroll-area/index.js";
|
|
46
|
+
export * from "./resize/index.js";
|
|
47
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * KRDS(디지털정부서비스 UI/UX 가이드라인) React 부품.\n *\n * 두 갈래다.\n * 1. **KRDS 부품** — 킷(`krds-uiux`)의 클래스를 그대로 쓰는 껍데기.\n * 모양·색·크기는 전부 킷에서 오고, 이 층은 마크업 구조와 접근성 속성을 고정한다.\n * 2. **보충 부품** — KRDS 가 다루지 않는 업무시스템(백오피스)용. KRDS 토큰으로 그린다.\n * KRDS 는 국민용 누리집이 대상이라 표 정렬·콤보박스·서랍·토스트 같은 것이 없다.\n *\n */\n\n/* ── 공통 타입 ─────────────────────────────────────────────── */\nexport * from './lib/krds';\n\n/* ── 액션 ─────────────────────────────────────────────────── */\nexport * from './button';\n\n/* ── 폼 ───────────────────────────────────────────────────── */\nexport * from './field';\nexport * from './text-input';\nexport * from './textarea';\nexport * from './select';\nexport * from './checkbox';\nexport * from './radio';\nexport * from './file-upload';\nexport * from './combobox';\nexport * from './input-group';\n\n/* ── 표현 ─────────────────────────────────────────────────── */\nexport * from './badge';\nexport * from './tag';\nexport * from './table';\nexport * from './data-table';\nexport * from './structured-list';\nexport * from './text-list';\nexport * from './critical-alert';\nexport * from './alert';\nexport * from './empty-state';\n\n/* ── 탐색 ─────────────────────────────────────────────────── */\nexport * from './skip-link';\nexport * from './masthead';\nexport * from './header';\nexport * from './main-menu';\nexport * from './breadcrumb';\nexport * from './side-navigation';\nexport * from './in-page-navigation';\nexport * from './pagination';\nexport * from './footer';\nexport * from './identifier';\n\n/* ── 열고 닫기 ─────────────────────────────────────────────── */\nexport * from './accordion';\nexport * from './disclosure';\nexport * from './tab';\nexport * from './modal';\nexport * from './drawer';\nexport * from './tooltip';\nexport * from './context-menu';\n\n/* ── 피드백 ───────────────────────────────────────────────── */\nexport * from './spinner';\nexport * from './step-indicator';\nexport * from './progress';\nexport * from './skeleton';\nexport * from './toast';\n\n/* ── 그 밖 ────────────────────────────────────────────────── */\nexport * from './avatar';\nexport * from './separator';\nexport * from './scroll-area';\nexport * from './resize';\n"],"mappings":"AAYA,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ComponentPropsWithRef, ReactNode } from 'react';
|
|
2
|
+
import { type KrdsFieldSize } from '../lib/krds';
|
|
3
|
+
export type InputGroupProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {
|
|
4
|
+
/** 묶음 전체의 크기. 기본 medium(48px). **자식이 저마다 적지 않고 여기서 한 번만 정한다.** */
|
|
5
|
+
size?: KrdsFieldSize;
|
|
6
|
+
/** 세로 정렬. 기본 `center` — 자식이 레이블까지 달고 있으면 `end` 로 밑선을 맞춘다. */
|
|
7
|
+
align?: 'center' | 'end';
|
|
8
|
+
/** 간격을 16px 에서 8px 로 좁힌다. 입력칸과 버튼을 한 덩어리처럼 보이게 할 때. */
|
|
9
|
+
tight?: boolean;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* `<div class="input-group">` — 컨트롤 하나와 그에 딸린 버튼·기호를 한 줄로 묶는다.
|
|
14
|
+
* `size` 를 하나 바꾸면 입력칸·버튼이 함께 움직인다.
|
|
15
|
+
*
|
|
16
|
+
* **`className="row"` 안에서는 세로로 쌓인다** — 킷에 그런 규칙이 있다.
|
|
17
|
+
*/
|
|
18
|
+
export declare function InputGroup({ size, align, tight, className, children, ...rest }: InputGroupProps): import("react").JSX.Element;
|
|
19
|
+
export type InputGroupItemProps = ComponentPropsWithRef<'div'> & {
|
|
20
|
+
/** 안쪽 자식에 내려보낸다. 보통 {@link InputGroup} 이 알아서 넣어 준다. */
|
|
21
|
+
size?: KrdsFieldSize;
|
|
22
|
+
/** 남는 폭을 차지한다. 기본 true — 이 상자에 넣는 것이 대개 입력칸이라서다. */
|
|
23
|
+
grow?: boolean;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* 늘어나는 칸. **입력칸은 이 상자로 감싼다** — 감싸지 않으면 기준 폭이 컨테이너 전체가 되어
|
|
27
|
+
* 옆 버튼을 찌그러뜨린다. 늘어나지 않아야 하면 `grow={false}`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function InputGroupItem({ size, grow, className, children, ...rest }: InputGroupItemProps): import("react").JSX.Element;
|
|
30
|
+
export type InputGroupTextProps = ComponentPropsWithRef<'span'> & {
|
|
31
|
+
/** 보통 {@link InputGroup} 이 넣어 준다. 기본 medium(48px). */
|
|
32
|
+
size?: KrdsFieldSize;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* 입력칸에 붙는 글자 — 접두사(`https://`), 단위(`원`·`건`), 구분 기호(`~`).
|
|
36
|
+
* 킷에 클래스가 없어 보충했다. 입력칸과 같은 높이로 서서 밑선이 맞는다.
|
|
37
|
+
*
|
|
38
|
+
* 「원」처럼 **뜻이 있는 단위는 읽혀야 하고**, `~` 처럼 시각적 기호는 `aria-hidden` 을 직접 붙여
|
|
39
|
+
* 소리에서 뺀다 — 어느 쪽인지는 부품이 판단할 수 없어 기본값을 두지 않았다.
|
|
40
|
+
*/
|
|
41
|
+
export declare function InputGroupText({ size, className, children, ...rest }: InputGroupTextProps): import("react").JSX.Element;
|
|
42
|
+
/** 기간 입력이 쓰는 크기. 킷에 구분 기호의 xlarge 규칙이 없어 덜어냈다. */
|
|
43
|
+
export type InputGroupRangeSize = Exclude<KrdsFieldSize, 'xlarge'>;
|
|
44
|
+
export type InputGroupRangeProps = Omit<ComponentPropsWithRef<'ul'>, 'children'> & {
|
|
45
|
+
/** 기본 medium(48px). 두 칸과 가운데 기호에 함께 내려간다. */
|
|
46
|
+
size?: InputGroupRangeSize;
|
|
47
|
+
/** 시작 칸. 레이블까지 갖춘 필드를 그대로 넣어도 된다. */
|
|
48
|
+
from: ReactNode;
|
|
49
|
+
/** 끝 칸. */
|
|
50
|
+
to: ReactNode;
|
|
51
|
+
/** 가운데 구분 기호. 기본 `-`. 장식이라 소리에서는 빠진다. */
|
|
52
|
+
separator?: ReactNode;
|
|
53
|
+
/** 킷 달력 스크립트가 잡는 손잡이 클래스(`set`)를 붙인다. 킷 달력을 물릴 때만 켠다. */
|
|
54
|
+
set?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* 기간 입력 — 킷 `input-group range`. 두 칸이 폭을 반씩 나눠 갖고 입력칸 밑선이 맞는다.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* <InputGroupRange
|
|
61
|
+
* from={<TextField id="from" label="시작일" type="date" />}
|
|
62
|
+
* to={<TextField id="to" label="종료일" type="date" />}
|
|
63
|
+
* />
|
|
64
|
+
*/
|
|
65
|
+
export declare function InputGroupRange({ size, from, to, separator, set, className, ...rest }: InputGroupRangeProps): import("react").JSX.Element;
|
|
66
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/input-group/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAC;AAC5E,OAAO,EAAM,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AA6CrD,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,GAAG;IAC7E,kEAAkE;IAClE,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,QAAQ,GAAG,KAAK,CAAC;IACzB,uDAAuD;IACvD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACzB,IAAe,EACf,KAAgB,EAChB,KAAK,EACL,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,eAAe,+BASjB;AAID,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,KAAK,CAAC,GAAG;IAC/D,uDAAuD;IACvD,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,oDAAoD;IACpD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAe,EACf,IAAW,EACX,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,mBAAmB,+BAMrB;AAYD,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,MAAM,CAAC,GAAG;IAChE,sDAAsD;IACtD,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,IAAe,EACf,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACR,EAAE,mBAAmB,+BAarB;AAID,kDAAkD;AAClD,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AAEnE,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,GAAG;IACjF,6CAA6C;IAC7C,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,qCAAqC;IACrC,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW;IACX,EAAE,EAAE,SAAS,CAAC;IACd,yCAAyC;IACzC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,yDAAyD;IACzD,GAAG,CAAC,EAAE,OAAO,CAAC;CACf,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,EAC9B,IAAe,EACf,IAAI,EACJ,EAAE,EACF,SAAe,EACf,GAAG,EACH,SAAS,EACT,GAAG,IAAI,EACR,EAAE,oBAAoB,+BAWtB"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Children, cloneElement, Fragment, isValidElement } from "react";
|
|
3
|
+
import { cx } from "../lib/krds.js";
|
|
4
|
+
function withSize(children, size) {
|
|
5
|
+
return Children.map(children, (child) => {
|
|
6
|
+
if (!isValidElement(child)) return child;
|
|
7
|
+
if (typeof child.type === "string" || child.type === Fragment) return child;
|
|
8
|
+
const props = child.props;
|
|
9
|
+
if (props.size !== void 0) return child;
|
|
10
|
+
return cloneElement(child, { size });
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function InputGroup({
|
|
14
|
+
size = "medium",
|
|
15
|
+
align = "center",
|
|
16
|
+
tight,
|
|
17
|
+
className,
|
|
18
|
+
children,
|
|
19
|
+
...rest
|
|
20
|
+
}) {
|
|
21
|
+
return /* @__PURE__ */ jsx(
|
|
22
|
+
"div",
|
|
23
|
+
{
|
|
24
|
+
className: cx("input-group", align === "end" && "items-end", tight && "gap-2", className),
|
|
25
|
+
...rest,
|
|
26
|
+
children: withSize(children, size)
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function InputGroupItem({
|
|
31
|
+
size = "medium",
|
|
32
|
+
grow = true,
|
|
33
|
+
className,
|
|
34
|
+
children,
|
|
35
|
+
...rest
|
|
36
|
+
}) {
|
|
37
|
+
return /* @__PURE__ */ jsx("div", { className: cx(grow ? "min-w-0 flex-1" : "shrink-0", className), ...rest, children: withSize(children, size) });
|
|
38
|
+
}
|
|
39
|
+
const ADDON_BOX = {
|
|
40
|
+
small: "h-10 text-label-sm",
|
|
41
|
+
medium: "h-12 text-label-md",
|
|
42
|
+
large: "h-14 text-label-lg",
|
|
43
|
+
xlarge: "h-20 text-heading-md"
|
|
44
|
+
};
|
|
45
|
+
function InputGroupText({
|
|
46
|
+
size = "medium",
|
|
47
|
+
className,
|
|
48
|
+
children,
|
|
49
|
+
...rest
|
|
50
|
+
}) {
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
"span",
|
|
53
|
+
{
|
|
54
|
+
className: cx(
|
|
55
|
+
"inline-flex shrink-0 items-center whitespace-nowrap text-fg-subtle",
|
|
56
|
+
ADDON_BOX[size],
|
|
57
|
+
className
|
|
58
|
+
),
|
|
59
|
+
...rest,
|
|
60
|
+
children
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function InputGroupRange({
|
|
65
|
+
size = "medium",
|
|
66
|
+
from,
|
|
67
|
+
to,
|
|
68
|
+
separator = "-",
|
|
69
|
+
set,
|
|
70
|
+
className,
|
|
71
|
+
...rest
|
|
72
|
+
}) {
|
|
73
|
+
return /* @__PURE__ */ jsxs("ul", { className: cx("input-group range", set && "set", className), ...rest, children: [
|
|
74
|
+
/* @__PURE__ */ jsx("li", { children: withSize(from, size) }),
|
|
75
|
+
/* @__PURE__ */ jsx("li", { className: cx("mark", size), "aria-hidden": "true", children: separator }),
|
|
76
|
+
/* @__PURE__ */ jsx("li", { children: withSize(to, size) })
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
InputGroup,
|
|
81
|
+
InputGroupItem,
|
|
82
|
+
InputGroupRange,
|
|
83
|
+
InputGroupText
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/input-group/index.tsx"],"sourcesContent":["import { Children, cloneElement, Fragment, isValidElement } from 'react';\nimport type { ComponentPropsWithRef, ReactElement, ReactNode } from 'react';\nimport { cx, type KrdsFieldSize } from '../lib/krds';\n\n/**\n * 입력 묶음 — 킷 `input-group`.\n *\n * 컨트롤 하나와 그에 딸린 버튼·기호를 한 줄로 묶고 **크기를 한 번만 정해 자식에게 내려보낸다.**\n * 그래서 입력칸과 옆 버튼의 높이가 어긋나지 않는다(기본 medium · 48px).\n * 레이블까지 갖춘 필드 여럿을 가로로 늘어놓는 것은 `ui/field` 의 `FieldRow` 다.\n * 기간(시작~끝)은 {@link InputGroupRange} 를 쓴다.\n * 크기는 `TextInput`·`Select`·`Button` 같은 **부품**에만 내려간다 — 맨 `<input>` 은 그대로 남는다.\n *\n * @example\n * <InputGroup size=\"medium\">\n * <InputGroupItem>\n * <TextField id=\"q\" label=\"검색어\" hideLabel placeholder=\"검색어를 입력하세요\" />\n * </InputGroupItem>\n * <Button variant=\"primary\" type=\"submit\">검색</Button>\n * </InputGroup>\n * <InputGroup>\n * <InputGroupItem><TextInput id=\"amount\" inputMode=\"numeric\" /></InputGroupItem>\n * <InputGroupText>원</InputGroupText>\n * </InputGroup>\n *\n * 자세히: docs/krds/09-부품-노트.md#입력묶음\n */\n\n/* ────────────────────────────── 크기 전파 ────────────────────────────── */\n\n/**\n * 자식 부품에 `size` 를 내려보낸다. DOM 태그와 Fragment 에는 넘기지 않고(HTML `size` 속성과 뜻이 다르다),\n * 자식이 이미 `size` 를 갖고 있으면 그대로 둔다.\n */\nfunction withSize(children: ReactNode, size: KrdsFieldSize): ReactNode {\n return Children.map(children, (child) => {\n if (!isValidElement(child)) return child;\n if (typeof child.type === 'string' || child.type === Fragment) return child;\n // React 19 타입에서 `props` 는 unknown 이라 좁혀서 본다.\n const props = child.props as { size?: unknown };\n if (props.size !== undefined) return child;\n return cloneElement(child as ReactElement<{ size?: KrdsFieldSize }>, { size });\n });\n}\n\n/* ────────────────────────────── 기본형 ────────────────────────────── */\n\nexport type InputGroupProps = Omit<ComponentPropsWithRef<'div'>, 'children'> & {\n /** 묶음 전체의 크기. 기본 medium(48px). **자식이 저마다 적지 않고 여기서 한 번만 정한다.** */\n size?: KrdsFieldSize;\n /** 세로 정렬. 기본 `center` — 자식이 레이블까지 달고 있으면 `end` 로 밑선을 맞춘다. */\n align?: 'center' | 'end';\n /** 간격을 16px 에서 8px 로 좁힌다. 입력칸과 버튼을 한 덩어리처럼 보이게 할 때. */\n tight?: boolean;\n children?: ReactNode;\n};\n\n/**\n * `<div class=\"input-group\">` — 컨트롤 하나와 그에 딸린 버튼·기호를 한 줄로 묶는다.\n * `size` 를 하나 바꾸면 입력칸·버튼이 함께 움직인다.\n *\n * **`className=\"row\"` 안에서는 세로로 쌓인다** — 킷에 그런 규칙이 있다.\n */\nexport function InputGroup({\n size = 'medium',\n align = 'center',\n tight,\n className,\n children,\n ...rest\n}: InputGroupProps) {\n return (\n <div\n className={cx('input-group', align === 'end' && 'items-end', tight && 'gap-2', className)}\n {...rest}\n >\n {withSize(children, size)}\n </div>\n );\n}\n\n/* ────────────────────────────── 폭을 차지하는 칸 ────────────────────────────── */\n\nexport type InputGroupItemProps = ComponentPropsWithRef<'div'> & {\n /** 안쪽 자식에 내려보낸다. 보통 {@link InputGroup} 이 알아서 넣어 준다. */\n size?: KrdsFieldSize;\n /** 남는 폭을 차지한다. 기본 true — 이 상자에 넣는 것이 대개 입력칸이라서다. */\n grow?: boolean;\n};\n\n/**\n * 늘어나는 칸. **입력칸은 이 상자로 감싼다** — 감싸지 않으면 기준 폭이 컨테이너 전체가 되어\n * 옆 버튼을 찌그러뜨린다. 늘어나지 않아야 하면 `grow={false}`.\n */\nexport function InputGroupItem({\n size = 'medium',\n grow = true,\n className,\n children,\n ...rest\n}: InputGroupItemProps) {\n return (\n <div className={cx(grow ? 'min-w-0 flex-1' : 'shrink-0', className)} {...rest}>\n {withSize(children, size)}\n </div>\n );\n}\n\n/* ────────────────────────────── 접두사·접미사 ────────────────────────────── */\n\n/** 크기별 상자 높이와 글자 크기. 킷 입력칸 토큰과 같은 값이다(40 / 48 / 56 / 80px). */\nconst ADDON_BOX: Record<KrdsFieldSize, string> = {\n small: 'h-10 text-label-sm',\n medium: 'h-12 text-label-md',\n large: 'h-14 text-label-lg',\n xlarge: 'h-20 text-heading-md',\n};\n\nexport type InputGroupTextProps = ComponentPropsWithRef<'span'> & {\n /** 보통 {@link InputGroup} 이 넣어 준다. 기본 medium(48px). */\n size?: KrdsFieldSize;\n};\n\n/**\n * 입력칸에 붙는 글자 — 접두사(`https://`), 단위(`원`·`건`), 구분 기호(`~`).\n * 킷에 클래스가 없어 보충했다. 입력칸과 같은 높이로 서서 밑선이 맞는다.\n *\n * 「원」처럼 **뜻이 있는 단위는 읽혀야 하고**, `~` 처럼 시각적 기호는 `aria-hidden` 을 직접 붙여\n * 소리에서 뺀다 — 어느 쪽인지는 부품이 판단할 수 없어 기본값을 두지 않았다.\n */\nexport function InputGroupText({\n size = 'medium',\n className,\n children,\n ...rest\n}: InputGroupTextProps) {\n return (\n <span\n className={cx(\n 'inline-flex shrink-0 items-center whitespace-nowrap text-fg-subtle',\n ADDON_BOX[size],\n className,\n )}\n {...rest}\n >\n {children}\n </span>\n );\n}\n\n/* ────────────────────────────── 기간 입력 ────────────────────────────── */\n\n/** 기간 입력이 쓰는 크기. 킷에 구분 기호의 xlarge 규칙이 없어 덜어냈다. */\nexport type InputGroupRangeSize = Exclude<KrdsFieldSize, 'xlarge'>;\n\nexport type InputGroupRangeProps = Omit<ComponentPropsWithRef<'ul'>, 'children'> & {\n /** 기본 medium(48px). 두 칸과 가운데 기호에 함께 내려간다. */\n size?: InputGroupRangeSize;\n /** 시작 칸. 레이블까지 갖춘 필드를 그대로 넣어도 된다. */\n from: ReactNode;\n /** 끝 칸. */\n to: ReactNode;\n /** 가운데 구분 기호. 기본 `-`. 장식이라 소리에서는 빠진다. */\n separator?: ReactNode;\n /** 킷 달력 스크립트가 잡는 손잡이 클래스(`set`)를 붙인다. 킷 달력을 물릴 때만 켠다. */\n set?: boolean;\n};\n\n/**\n * 기간 입력 — 킷 `input-group range`. 두 칸이 폭을 반씩 나눠 갖고 입력칸 밑선이 맞는다.\n *\n * @example\n * <InputGroupRange\n * from={<TextField id=\"from\" label=\"시작일\" type=\"date\" />}\n * to={<TextField id=\"to\" label=\"종료일\" type=\"date\" />}\n * />\n */\nexport function InputGroupRange({\n size = 'medium',\n from,\n to,\n separator = '-',\n set,\n className,\n ...rest\n}: InputGroupRangeProps) {\n return (\n <ul className={cx('input-group range', set && 'set', className)} {...rest}>\n <li>{withSize(from, size)}</li>\n {/* 크기 클래스를 반드시 붙인다 — 빼면 킷 기본 56px 이라 medium 입력 사이에서 혼자 커진다. */}\n <li className={cx('mark', size)} aria-hidden=\"true\">\n {separator}\n </li>\n <li>{withSize(to, size)}</li>\n </ul>\n );\n}\n"],"mappings":"AAwEI,cAmHA,YAnHA;AAxEJ,SAAS,UAAU,cAAc,UAAU,sBAAsB;AAEjE,SAAS,UAA8B;AAgCvC,SAAS,SAAS,UAAqB,MAAgC;AACrE,SAAO,SAAS,IAAI,UAAU,CAAC,UAAU;AACvC,QAAI,CAAC,eAAe,KAAK,EAAG,QAAO;AACnC,QAAI,OAAO,MAAM,SAAS,YAAY,MAAM,SAAS,SAAU,QAAO;AAEtE,UAAM,QAAQ,MAAM;AACpB,QAAI,MAAM,SAAS,OAAW,QAAO;AACrC,WAAO,aAAa,OAAiD,EAAE,KAAK,CAAC;AAAA,EAC/E,CAAC;AACH;AAoBO,SAAS,WAAW;AAAA,EACzB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAoB;AAClB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,GAAG,eAAe,UAAU,SAAS,aAAa,SAAS,SAAS,SAAS;AAAA,MACvF,GAAG;AAAA,MAEH,mBAAS,UAAU,IAAI;AAAA;AAAA,EAC1B;AAEJ;AAeO,SAAS,eAAe;AAAA,EAC7B,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAwB;AACtB,SACE,oBAAC,SAAI,WAAW,GAAG,OAAO,mBAAmB,YAAY,SAAS,GAAI,GAAG,MACtE,mBAAS,UAAU,IAAI,GAC1B;AAEJ;AAKA,MAAM,YAA2C;AAAA,EAC/C,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AACV;AAcO,SAAS,eAAe;AAAA,EAC7B,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAwB;AACtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,UAAU,IAAI;AAAA,QACd;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AA6BO,SAAS,gBAAgB;AAAA,EAC9B,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAyB;AACvB,SACE,qBAAC,QAAG,WAAW,GAAG,qBAAqB,OAAO,OAAO,SAAS,GAAI,GAAG,MACnE;AAAA,wBAAC,QAAI,mBAAS,MAAM,IAAI,GAAE;AAAA,IAE1B,oBAAC,QAAG,WAAW,GAAG,QAAQ,IAAI,GAAG,eAAY,QAC1C,qBACH;AAAA,IACA,oBAAC,QAAI,mBAAS,IAAI,IAAI,GAAE;AAAA,KAC1B;AAEJ;","names":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KRDS 공통 타입과 도우미.
|
|
3
|
+
*
|
|
4
|
+
* 여기 있는 값은 전부 킷(`public/krds/krds.min.css`)에서 실측한 것이다. 지어낸 값이 없다.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* 크기.
|
|
8
|
+
*
|
|
9
|
+
* ★ 버튼과 입력칸이 **같은 이름에 같은 높이**다. 실측(1rem = 10px):
|
|
10
|
+
*
|
|
11
|
+
* | 이름 | 높이 | 버튼 | 입력·셀렉트 |
|
|
12
|
+
* |---------|-----|------|------------|
|
|
13
|
+
* | xsmall | 32 | ○ | — |
|
|
14
|
+
* | small | 40 | ○ | ○ |
|
|
15
|
+
* | medium | 48 | ○ | ○ |
|
|
16
|
+
* | large | 56 | ○ | ○ |
|
|
17
|
+
* | xlarge | 64 | ○ | 80(예외) |
|
|
18
|
+
*
|
|
19
|
+
* 그래서 한 줄에 놓을 버튼과 입력칸에는 **같은 size 를 준다.**
|
|
20
|
+
* 예전에 입력칸에 크기를 안 줘서 킷 기본값 large(56)로 렌더되고 옆 버튼은 small(40)이라
|
|
21
|
+
* 16px 이 어긋났다 — 부품에서 기본값을 명시해 이 실수를 구조적으로 막는다.
|
|
22
|
+
*/
|
|
23
|
+
export type KrdsSize = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
24
|
+
/** 폼 요소가 실제로 지원하는 크기(킷에 xsmall 규칙이 없다). */
|
|
25
|
+
export type KrdsFieldSize = Exclude<KrdsSize, 'xsmall'>;
|
|
26
|
+
/**
|
|
27
|
+
* 의미 색상. 킷의 배지·태그·알림이 공통으로 쓰는 이름이다.
|
|
28
|
+
*/
|
|
29
|
+
export type KrdsTone = 'primary' | 'secondary' | 'gray' | 'point' | 'danger' | 'warning' | 'success' | 'information';
|
|
30
|
+
/** 폼 필드 상태. 킷 클래스는 `.is-error` · `.is-success` · `.is-information`. */
|
|
31
|
+
export type KrdsFieldState = 'error' | 'success' | 'information';
|
|
32
|
+
/**
|
|
33
|
+
* 킷이 제공하는 아이콘 이름(`ico-` 접두사를 뺀 것).
|
|
34
|
+
* `<i className="svg-icon ico-sch" />` 처럼 쓴다.
|
|
35
|
+
*
|
|
36
|
+
* 킷의 아이콘 파일은 `public/krds/img/component/icon/ico_*.svg` 에 있고 CSS 가 mask 로 물린다.
|
|
37
|
+
* **여기 없는 이름을 쓰면 검은 네모가 나온다** — 그래서 타입으로 좁혀 둔다.
|
|
38
|
+
* 아래 목록은 킷 CSS 에서 `\\.ico-([a-z0-9-]+)` 로 뽑은 68종 전부다. 킷을 올릴 때 다시 뽑는다.
|
|
39
|
+
*/
|
|
40
|
+
export type KrdsIcon = 'all' | 'angle' | 'blog' | 'bread-home' | 'cal-move' | 'calendar' | 'call' | 'checkbox' | 'complete-fill' | 'del' | 'delete-fill' | 'down' | 'ellipsis' | 'email' | 'error-fill' | 'facebook' | 'faq' | 'file' | 'filter' | 'flag' | 'fold' | 'func' | 'global' | 'go' | 'go-top' | 'help' | 'information-fill' | 'instagram' | 'invalid' | 'join' | 'like' | 'link' | 'log' | 'login-go' | 'login-type01' | 'login-type02' | 'login-type03' | 'login-type04' | 'login-type05' | 'login-type06' | 'logout' | 'modal-close' | 'more' | 'my' | 'page-next' | 'plus' | 'popup-close' | 'print' | 'pw-visible' | 'pw-visible-on' | 'refresh' | 'reset' | 'sch' | 'sch-plus' | 'scrap' | 'search' | 'setting' | 'share' | 'sns-x' | 'success-fill' | 'swiper-play' | 'swiper-stop' | 'toggle' | 'tooltip' | 'upload' | 'urgent-danger' | 'view-mode' | 'youtube';
|
|
41
|
+
/** 클래스 이름을 합친다. falsy 는 버린다. */
|
|
42
|
+
export declare function cx(...parts: Array<string | false | null | undefined>): string;
|
|
43
|
+
/** 상태 → 킷 클래스. */
|
|
44
|
+
export declare function stateClass(state?: KrdsFieldState): string | undefined;
|
|
45
|
+
//# sourceMappingURL=krds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"krds.d.ts","sourceRoot":"","sources":["../../src/lib/krds.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE1E,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAChB,SAAS,GACT,WAAW,GACX,MAAM,GACN,OAAO,GACP,QAAQ,GACR,SAAS,GACT,SAAS,GACT,aAAa,CAAC;AAElB,uEAAuE;AACvE,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAChB,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU,GACjE,MAAM,GAAG,UAAU,GAAG,eAAe,GAAG,KAAK,GAAG,aAAa,GAAG,MAAM,GACtE,UAAU,GAAG,OAAO,GAAG,YAAY,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,GACjE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,GACrD,QAAQ,GAAG,MAAM,GAAG,kBAAkB,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,GACzE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,GAAG,cAAc,GAAG,cAAc,GACtE,cAAc,GAAG,cAAc,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,GAAG,aAAa,GAC5F,MAAM,GAAG,IAAI,GAAG,WAAW,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,GAC9D,YAAY,GAAG,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,UAAU,GACzE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,GAAG,cAAc,GACnE,aAAa,GAAG,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,eAAe,GACjF,WAAW,GAAG,SAAS,CAAC;AAE5B,gCAAgC;AAChC,wBAAgB,EAAE,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,MAAM,CAE7E;AAED,kBAAkB;AAClB,wBAAgB,UAAU,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAErE"}
|