@uniai-fe/uds-templates 0.0.1

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/README.md ADDED
@@ -0,0 +1,126 @@
1
+ # @uniai-fe/uds-templates
2
+
3
+ `@uniai-fe/uds-foundation` 토큰과 `@uniai-fe/uds-primitives` 기초 컴포넌트를 조합해,
4
+ 서비스 앱에서 자주 반복되는 **화면/플로우 템플릿**을 제공하는 패키지입니다.
5
+
6
+ Next.js 서비스에서 primitives와 동일한 방식으로 **Raw TypeScript** 형태로 소비되며,
7
+ `pnpm transpilePackages` 설정을 통해 빌드 파이프라인에 포함됩니다.
8
+
9
+ ## Peer Dependencies
10
+
11
+ - `@uniai-fe/uds-foundation` (CSS 토큰 + reset)
12
+ - `react` `>= 19`, `react-dom` `>= 19`
13
+ - Storybook/로컬 개발용으로는 `@uniai-fe/uds-foundation/css`를 앱 루트에서 한 번만 import 해야 합니다.
14
+
15
+ ## 특징
16
+
17
+ - 화면 단위 템플릿:
18
+ - 로그인/회원가입/아이디·비밀번호 찾기 등 `/auth/**`
19
+ - 로그인 이후 화면을 위한 모바일 페이지 프레임 `/page-frame/mobile`(PC는 추후 `/page-frame/pc`)
20
+ - Raw TS 배포:
21
+ - `package.json` `exports`가 `src/**`를 가리키며, 번들 없이 즉시 import 가능한 형태로 제공합니다.
22
+ - 디자인시스템 일관성:
23
+ - 스타일은 항상 `@uniai-fe/uds-foundation/css`에서 제공하는 CSS 변수(토큰)를 통해만 정의됩니다.
24
+ - UI 요소는 모두 `@uniai-fe/uds-primitives` 컴포넌트를 조합해 구성합니다.
25
+ - 역할 분리:
26
+ - **templates**는 레이아웃/플로우/상태 표현까지 담당하고,
27
+ - API 호출, 인증 토큰 관리, 라우팅, i18n 등 비즈니스 로직은 서비스 앱에서 구현합니다.
28
+
29
+ ## 현재 제공 템플릿
30
+
31
+ - `/auth/**`
32
+ - 로그인 화면 템플릿 (Login)
33
+ - 회원가입 플로우 템플릿 (Sign Up, step 기반)
34
+ - 아이디 찾기 템플릿
35
+ - 비밀번호 찾기/재설정 템플릿
36
+ - `/page-frame/**`
37
+ - `/page-frame/mobile`: 로그인 이후 화면을 위한 모바일 프레임(header/body/footer)
38
+ - `/page-frame/pc`: 추후 확장 예정
39
+
40
+ 각 템플릿의 상세한 범위와 의사결정은 `CONTEXT-*.md` 문서에서 관리합니다.
41
+
42
+ ## 설치 & 기본 설정(Next.js 예시)
43
+
44
+ ```bash
45
+ pnpm add @uniai-fe/uds-foundation @uniai-fe/uds-primitives @uniai-fe/uds-templates
46
+ ```
47
+
48
+ ### 1) transpilePackages 설정
49
+
50
+ ```ts
51
+ // next.config.ts
52
+ const nextConfig = {
53
+ transpilePackages: [
54
+ "@uniai-fe/uds-foundation",
55
+ "@uniai-fe/uds-primitives",
56
+ "@uniai-fe/uds-templates",
57
+ ],
58
+ };
59
+
60
+ export default nextConfig;
61
+ ```
62
+
63
+ ### 2) foundation css 전역 주입
64
+
65
+ `@uniai-fe/uds-foundation/css`는 앱 루트에서 **1회만** import 합니다.
66
+
67
+ 예시(App Router):
68
+
69
+ ```ts
70
+ // app/layout.tsx
71
+ import "@uniai-fe/uds-foundation/css";
72
+
73
+ export default function RootLayout(props: { children: React.ReactNode }) {
74
+ return (
75
+ <html lang="ko">
76
+ <body>{props.children}</body>
77
+ </html>
78
+ );
79
+ }
80
+ ```
81
+
82
+ ## 간단 사용 예시
83
+
84
+ ```tsx
85
+ // app/(auth)/login/page.tsx
86
+ import { AuthLoginTemplate } from "@uniai-fe/uds-templates/auth";
87
+
88
+ export default function LoginPage() {
89
+ return (
90
+ <AuthLoginTemplate
91
+ text={{
92
+ title: "로그인",
93
+ description: "서비스 이용을 위해 로그인해 주세요.",
94
+ }}
95
+ logoSlot={<img src="/logo.svg" alt="서비스 로고" />}
96
+ onSubmit={async (values) => {
97
+ // 실제 로그인 API 호출은 서비스 앱에서 구현
98
+ // await login(values);
99
+ }}
100
+ />
101
+ );
102
+ }
103
+ ```
104
+
105
+ > 위 예시는 개념을 설명하기 위한 형태이며,
106
+ > 실제 props 구조/이름은 `CONTEXT-AUTH.md`에서 확정·관리합니다.
107
+
108
+ ## Scripts
109
+
110
+ 패키지 루트에서 실행 가능한 대표 명령:
111
+
112
+ - `pnpm module:lint` — ESLint 전역 규칙 점검
113
+ - `pnpm module:typecheck` — TS 타입 검사 (`tsconfig.build.json` 기준)
114
+ - `pnpm module:build` — lint → typecheck 순으로 배포 전 검증
115
+ - `pnpm design-templates:dev` — 타입 검사 watch 모드
116
+
117
+ 모노레포 루트에서는 `pnpm --filter @uniai-fe/uds-templates <command>` 또는 `pnpm modules:build`(turbo run)으로 전체 패키지를 빌드할 수 있습니다.
118
+
119
+ ## 문서
120
+
121
+ - `CONTEXT.md` — templates 패키지 전체 상황/진행 현황 인덱스
122
+ - `CONTEXT-GUIDELINES.md` — templates 전역 규칙(슬롯/반응형/page-frame 적용 범위 등)
123
+ - `CONTEXT-AUTH.md` — `/auth/**` 템플릿 범위/디자인 근거/Figma 링크
124
+ - `CONTEXT-PAGE-FRAME.md` — `/page-frame/**` 템플릿 범위/디자인 근거
125
+ - `FOUNDATION-USAGE-GUIDE.md` — 템플릿에서 design-foundation 토큰을 사용하는 규칙
126
+ - `PRIMITIVES-USAGE-GUIDE.md` — 템플릿에서 design-primitives를 사용하는 규칙
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@uniai-fe/uds-templates",
3
+ "version": "0.0.1",
4
+ "description": "UNIAI Design System; UI Templates Package",
5
+ "type": "module",
6
+ "private": false,
7
+ "sideEffects": [
8
+ "./src/**/*.scss"
9
+ ],
10
+ "license": "MIT",
11
+ "homepage": "https://www.uniai.co.kr/",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "packageManager": "pnpm@10.25.0",
16
+ "engines": {
17
+ "node": ">=24",
18
+ "pnpm": ">=10"
19
+ },
20
+ "author": {
21
+ "name": "GraffitoRyu",
22
+ "email": "yth4135@naver.com",
23
+ "url": "https://github.com/GraffitoRyu"
24
+ },
25
+ "files": [
26
+ "src"
27
+ ],
28
+ "main": "./src/index.tsx",
29
+ "module": "./src/index.tsx",
30
+ "exports": {
31
+ ".": "./src/index.tsx"
32
+ },
33
+ "scripts": {
34
+ "format": "prettier --write .",
35
+ "format:check": "prettier --check .",
36
+ "lint": "eslint . --max-warnings=0",
37
+ "typecheck": "tsc --project tsconfig.build.json --noEmit",
38
+ "build": "pnpm format && pnpm typecheck",
39
+ "dev": "tsc --project tsconfig.build.json --watch --noEmit",
40
+ "module:lint": "pnpm lint",
41
+ "module:typecheck": "pnpm typecheck",
42
+ "module:build": "pnpm build",
43
+ "design-templates:build": "pnpm run build",
44
+ "design-templates:dev": "pnpm run dev"
45
+ },
46
+ "peerDependencies": {
47
+ "@uniai-fe/uds-foundation": "workspace:^0.0.1",
48
+ "react": ">= 19",
49
+ "react-dom": ">= 19"
50
+ },
51
+ "dependencies": {
52
+ "@uniai-fe/uds-primitives": "workspace:*",
53
+ "clsx": "^2.1.1",
54
+ "dayjs": "^1.11.19"
55
+ },
56
+ "devDependencies": {
57
+ "@uniai-fe/uds-foundation": "workspace:*",
58
+ "@svgr/webpack": "^8.1.0",
59
+ "@types/node": "^24.10.2",
60
+ "@types/react": "^19.2.7",
61
+ "@types/react-dom": "^19.2.3",
62
+ "@uniai-fe/eslint-config": "workspace:*",
63
+ "@uniai-fe/next-devkit": "workspace:*",
64
+ "@uniai-fe/tsconfig": "workspace:*",
65
+ "eslint": "^9.39.1",
66
+ "prettier": "^3.7.4",
67
+ "sass": "^1.95.0",
68
+ "react-hook-form": "^7.68.0",
69
+ "typescript": "~5.9.3"
70
+ }
71
+ }
@@ -0,0 +1,24 @@
1
+ import clsx from "clsx";
2
+ import type { PageFrameContainerProps } from "./types";
3
+
4
+ /**
5
+ * 페이지 공통 컨테이너; header/body/footer 영역을 순서대로 배치한다.
6
+ * @component
7
+ */
8
+ export function PageFrameContainer({
9
+ className,
10
+ header,
11
+ headerClassName,
12
+ bodyClassName,
13
+ footer,
14
+ footerClassName,
15
+ children,
16
+ }: PageFrameContainerProps) {
17
+ return (
18
+ <div className={clsx("page-frame-container", className)}>
19
+ <div className={clsx("page-frame-header", headerClassName)}>{header}</div>
20
+ <div className={clsx("page-frame-body", bodyClassName)}>{children}</div>
21
+ <div className={clsx("page-frame-footer", footerClassName)}>{footer}</div>
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,42 @@
1
+ :root {
2
+ --frame-device-height: 812px;
3
+ --frame-device-width: 480px;
4
+ --frame-safe-area-top: env(safe-area-inset-top, 0px);
5
+ --frame-safe-area-bottom: env(safe-area-inset-bottom, 0px);
6
+ --page-frame-height: min(100svh, 100dvh, var(--frame-device-height, 812px));
7
+ --page-frame-header-padding-top: var(--frame-safe-area-top);
8
+ --page-frame-footer-safe-area: var(--frame-safe-area-bottom);
9
+ --page-frame-max-width: var(
10
+ --frame-device-width,
11
+ 480px
12
+ ); // PC에선 지정 폭으로 중앙 정렬하고, 모바일에선 100%를 유지
13
+ }
14
+
15
+ .page-frame-container {
16
+ display: grid;
17
+ grid-template-rows: auto 1fr auto;
18
+ width: min(100%, var(--page-frame-max-width));
19
+ margin: 0 auto; // 폭이 남는 PC 환경에서만 중앙 정렬
20
+ height: var(--page-frame-height); // viewport 변동 시에도 정확한 높이를 강제
21
+ background-color: var(--color-common-100, #ffffff);
22
+ color: inherit;
23
+ }
24
+
25
+ .page-frame-header,
26
+ .page-frame-body,
27
+ .page-frame-footer {
28
+ width: 100%;
29
+ }
30
+
31
+ .page-frame-header {
32
+ padding-top: var(--page-frame-header-padding-top);
33
+ }
34
+
35
+ .page-frame-body {
36
+ min-height: 0;
37
+ overflow-y: auto;
38
+ }
39
+
40
+ .page-frame-footer {
41
+ padding-bottom: var(--page-frame-footer-safe-area);
42
+ }
@@ -0,0 +1,4 @@
1
+ import "./index.scss";
2
+
3
+ export { PageFrameContainer } from "./PageFrameContainer";
4
+ export type { PageFrameContainerProps } from "./types";
@@ -0,0 +1,11 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ export type PageFrameContainerProps = {
4
+ className?: string;
5
+ header?: ReactNode;
6
+ headerClassName?: string;
7
+ bodyClassName?: string;
8
+ footer?: ReactNode;
9
+ footerClassName?: string;
10
+ children: ReactNode;
11
+ };
@@ -0,0 +1,3 @@
1
+ export * from "./container";
2
+ export * from "./mobile";
3
+ export * from "./navigation";
@@ -0,0 +1,30 @@
1
+ import clsx from "clsx";
2
+ import { PageFrameContainer } from "../container";
3
+ import type { PageFrameMobileProps } from "./types";
4
+
5
+ /**
6
+ * 모바일 전용 Page Frame 래퍼; 공통 컨테이너 위에 모바일 클래스/스타일을 덧입힌다.
7
+ * @component
8
+ * @param {PageFrameMobileProps} props
9
+ */
10
+ export function PageFrameMobile({
11
+ className,
12
+ header,
13
+ headerClassName,
14
+ bodyClassName,
15
+ footer,
16
+ footerClassName,
17
+ ...rest
18
+ }: PageFrameMobileProps) {
19
+ return (
20
+ <PageFrameContainer
21
+ {...rest}
22
+ className={clsx("page-frame-mobile", className)}
23
+ header={header}
24
+ headerClassName={clsx("page-frame-mobile-header", headerClassName)}
25
+ bodyClassName={clsx("page-frame-mobile-body", bodyClassName)}
26
+ footer={footer}
27
+ footerClassName={clsx("page-frame-mobile-footer", footerClassName)}
28
+ />
29
+ );
30
+ }
@@ -0,0 +1,17 @@
1
+ .page-frame-mobile {
2
+ width: 100%;
3
+ height: 100%;
4
+ min-height: var(--page-frame-height, var(--frame-device-height, 812px));
5
+ max-height: 100dvh;
6
+ background-color: var(--color-common-100, #ffffff);
7
+ }
8
+
9
+ .page-frame-mobile-header,
10
+ .page-frame-mobile-body,
11
+ .page-frame-mobile-footer {
12
+ width: 100%;
13
+ }
14
+
15
+ .page-frame-mobile-body {
16
+ background-color: inherit;
17
+ }
@@ -0,0 +1,4 @@
1
+ import "./index.scss";
2
+
3
+ export { PageFrameMobile } from "./PageFrameMobile";
4
+ export type { PageFrameMobileProps } from "./types";
@@ -0,0 +1,3 @@
1
+ import type { PageFrameContainerProps } from "../container";
2
+
3
+ export type PageFrameMobileProps = PageFrameContainerProps;
@@ -0,0 +1,26 @@
1
+ import clsx from "clsx";
2
+ import {
3
+ BottomNavigation,
4
+ type BottomNavigationProps,
5
+ } from "@uniai-fe/uds-primitives";
6
+
7
+ export type PageFrameNavigationProps = BottomNavigationProps;
8
+
9
+ /**
10
+ * Page Frame 전용 navigation 래퍼.
11
+ * primitives BottomNavigation을 flow 레이아웃 안에서 사용하도록 fixed 옵션을 제거한다.
12
+ * @component
13
+ * @param {PageFrameNavigationProps} props
14
+ */
15
+ export function PageFrameNavigation({
16
+ className,
17
+ ...rest
18
+ }: PageFrameNavigationProps) {
19
+ return (
20
+ <BottomNavigation
21
+ {...rest}
22
+ fixed={false}
23
+ className={clsx("page-frame-navigation", className)}
24
+ />
25
+ );
26
+ }
@@ -0,0 +1,3 @@
1
+ .page-frame-navigation {
2
+ width: 100%;
3
+ }
@@ -0,0 +1,4 @@
1
+ import "./index.scss";
2
+
3
+ export { PageFrameNavigation } from "./PageFrameNavigation";
4
+ export type { PageFrameNavigationProps } from "./PageFrameNavigation";
package/src/index.tsx ADDED
@@ -0,0 +1 @@
1
+ export * from "./components/page-frame";