@hua-labs/hua-ux 0.1.0-alpha.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 +839 -0
- package/dist/framework/a11y/components/LiveRegion.d.ts +64 -0
- package/dist/framework/a11y/components/LiveRegion.d.ts.map +1 -0
- package/dist/framework/a11y/components/LiveRegion.js +43 -0
- package/dist/framework/a11y/components/SkipToContent.d.ts +62 -0
- package/dist/framework/a11y/components/SkipToContent.d.ts.map +1 -0
- package/dist/framework/a11y/components/SkipToContent.js +60 -0
- package/dist/framework/a11y/hooks/useFocusManagement.d.ts +60 -0
- package/dist/framework/a11y/hooks/useFocusManagement.d.ts.map +1 -0
- package/dist/framework/a11y/hooks/useFocusManagement.js +71 -0
- package/dist/framework/a11y/hooks/useFocusTrap.d.ts +64 -0
- package/dist/framework/a11y/hooks/useFocusTrap.d.ts.map +1 -0
- package/dist/framework/a11y/hooks/useFocusTrap.js +185 -0
- package/dist/framework/a11y/hooks/useLiveRegion.d.ts +56 -0
- package/dist/framework/a11y/hooks/useLiveRegion.d.ts.map +1 -0
- package/dist/framework/a11y/hooks/useLiveRegion.js +60 -0
- package/dist/framework/a11y/index.d.ts +16 -0
- package/dist/framework/a11y/index.d.ts.map +1 -0
- package/dist/framework/a11y/index.js +11 -0
- package/dist/framework/branding/context.d.ts +52 -0
- package/dist/framework/branding/context.d.ts.map +1 -0
- package/dist/framework/branding/context.js +96 -0
- package/dist/framework/branding/css-vars.d.ts +34 -0
- package/dist/framework/branding/css-vars.d.ts.map +1 -0
- package/dist/framework/branding/css-vars.js +95 -0
- package/dist/framework/branding/tailwind-config.d.ts +38 -0
- package/dist/framework/branding/tailwind-config.d.ts.map +1 -0
- package/dist/framework/branding/tailwind-config.js +66 -0
- package/dist/framework/components/BrandedButton.d.ts +53 -0
- package/dist/framework/components/BrandedButton.d.ts.map +1 -0
- package/dist/framework/components/BrandedButton.js +40 -0
- package/dist/framework/components/BrandedCard.d.ts +52 -0
- package/dist/framework/components/BrandedCard.d.ts.map +1 -0
- package/dist/framework/components/BrandedCard.js +73 -0
- package/dist/framework/components/ErrorBoundary.d.ts +92 -0
- package/dist/framework/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/framework/components/ErrorBoundary.js +121 -0
- package/dist/framework/components/HuaUxLayout.d.ts +29 -0
- package/dist/framework/components/HuaUxLayout.d.ts.map +1 -0
- package/dist/framework/components/HuaUxLayout.js +32 -0
- package/dist/framework/components/HuaUxPage.d.ts +48 -0
- package/dist/framework/components/HuaUxPage.d.ts.map +1 -0
- package/dist/framework/components/HuaUxPage.js +105 -0
- package/dist/framework/components/Providers.d.ts +17 -0
- package/dist/framework/components/Providers.d.ts.map +1 -0
- package/dist/framework/components/Providers.js +72 -0
- package/dist/framework/components/WelcomePage.d.ts +44 -0
- package/dist/framework/components/WelcomePage.d.ts.map +1 -0
- package/dist/framework/components/WelcomePage.js +80 -0
- package/dist/framework/config/index.d.ts +182 -0
- package/dist/framework/config/index.d.ts.map +1 -0
- package/dist/framework/config/index.js +329 -0
- package/dist/framework/config/merge.d.ts +26 -0
- package/dist/framework/config/merge.d.ts.map +1 -0
- package/dist/framework/config/merge.js +160 -0
- package/dist/framework/config/schema.d.ts +25 -0
- package/dist/framework/config/schema.d.ts.map +1 -0
- package/dist/framework/config/schema.js +122 -0
- package/dist/framework/hooks/useMotion.d.ts +45 -0
- package/dist/framework/hooks/useMotion.d.ts.map +1 -0
- package/dist/framework/hooks/useMotion.js +40 -0
- package/dist/framework/index.d.ts +37 -0
- package/dist/framework/index.d.ts.map +1 -0
- package/dist/framework/index.js +42 -0
- package/dist/framework/license/errors.d.ts +15 -0
- package/dist/framework/license/errors.d.ts.map +1 -0
- package/dist/framework/license/errors.js +52 -0
- package/dist/framework/license/index.d.ts +70 -0
- package/dist/framework/license/index.d.ts.map +1 -0
- package/dist/framework/license/index.js +124 -0
- package/dist/framework/license/loader.d.ts +26 -0
- package/dist/framework/license/loader.d.ts.map +1 -0
- package/dist/framework/license/loader.js +137 -0
- package/dist/framework/license/types.d.ts +67 -0
- package/dist/framework/license/types.d.ts.map +1 -0
- package/dist/framework/license/types.js +18 -0
- package/dist/framework/loading/components/SkeletonGroup.d.ts +44 -0
- package/dist/framework/loading/components/SkeletonGroup.d.ts.map +1 -0
- package/dist/framework/loading/components/SkeletonGroup.js +34 -0
- package/dist/framework/loading/components/SuspenseWrapper.d.ts +58 -0
- package/dist/framework/loading/components/SuspenseWrapper.d.ts.map +1 -0
- package/dist/framework/loading/components/SuspenseWrapper.js +40 -0
- package/dist/framework/loading/hoc/withSuspense.d.ts +46 -0
- package/dist/framework/loading/hoc/withSuspense.d.ts.map +1 -0
- package/dist/framework/loading/hoc/withSuspense.js +54 -0
- package/dist/framework/loading/hooks/useDelayedLoading.d.ts +56 -0
- package/dist/framework/loading/hooks/useDelayedLoading.d.ts.map +1 -0
- package/dist/framework/loading/hooks/useDelayedLoading.js +97 -0
- package/dist/framework/loading/hooks/useLoadingState.d.ts +69 -0
- package/dist/framework/loading/hooks/useLoadingState.d.ts.map +1 -0
- package/dist/framework/loading/hooks/useLoadingState.js +59 -0
- package/dist/framework/loading/index.d.ts +16 -0
- package/dist/framework/loading/index.d.ts.map +1 -0
- package/dist/framework/loading/index.js +13 -0
- package/dist/framework/middleware/i18n.d.ts +90 -0
- package/dist/framework/middleware/i18n.d.ts.map +1 -0
- package/dist/framework/middleware/i18n.js +99 -0
- package/dist/framework/plugins/index.d.ts +8 -0
- package/dist/framework/plugins/index.d.ts.map +1 -0
- package/dist/framework/plugins/index.js +6 -0
- package/dist/framework/plugins/registry.d.ts +95 -0
- package/dist/framework/plugins/registry.d.ts.map +1 -0
- package/dist/framework/plugins/registry.js +160 -0
- package/dist/framework/plugins/types.d.ts +97 -0
- package/dist/framework/plugins/types.d.ts.map +1 -0
- package/dist/framework/plugins/types.js +6 -0
- package/dist/framework/seo/geo/examples.d.ts +87 -0
- package/dist/framework/seo/geo/examples.d.ts.map +1 -0
- package/dist/framework/seo/geo/examples.js +295 -0
- package/dist/framework/seo/geo/generateGEOMetadata.d.ts +107 -0
- package/dist/framework/seo/geo/generateGEOMetadata.d.ts.map +1 -0
- package/dist/framework/seo/geo/generateGEOMetadata.js +404 -0
- package/dist/framework/seo/geo/index.d.ts +19 -0
- package/dist/framework/seo/geo/index.d.ts.map +1 -0
- package/dist/framework/seo/geo/index.js +21 -0
- package/dist/framework/seo/geo/presets.d.ts +52 -0
- package/dist/framework/seo/geo/presets.d.ts.map +1 -0
- package/dist/framework/seo/geo/presets.js +47 -0
- package/dist/framework/seo/geo/structuredData.d.ts +187 -0
- package/dist/framework/seo/geo/structuredData.d.ts.map +1 -0
- package/dist/framework/seo/geo/structuredData.js +354 -0
- package/dist/framework/seo/geo/test-utils.d.ts +78 -0
- package/dist/framework/seo/geo/test-utils.d.ts.map +1 -0
- package/dist/framework/seo/geo/test-utils.js +139 -0
- package/dist/framework/seo/geo/types.d.ts +225 -0
- package/dist/framework/seo/geo/types.d.ts.map +1 -0
- package/dist/framework/seo/geo/types.js +51 -0
- package/dist/framework/types/index.d.ts +577 -0
- package/dist/framework/types/index.d.ts.map +1 -0
- package/dist/framework/types/index.js +6 -0
- package/dist/framework/utils/data-fetching.d.ts +45 -0
- package/dist/framework/utils/data-fetching.d.ts.map +1 -0
- package/dist/framework/utils/data-fetching.js +74 -0
- package/dist/framework/utils/file-structure.d.ts +29 -0
- package/dist/framework/utils/file-structure.d.ts.map +1 -0
- package/dist/framework/utils/file-structure.js +72 -0
- package/dist/framework/utils/metadata.d.ts +109 -0
- package/dist/framework/utils/metadata.d.ts.map +1 -0
- package/dist/framework/utils/metadata.js +105 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/presets/index.d.ts +8 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +7 -0
- package/dist/presets/marketing.d.ts +41 -0
- package/dist/presets/marketing.d.ts.map +1 -0
- package/dist/presets/marketing.js +81 -0
- package/dist/presets/product.d.ts +41 -0
- package/dist/presets/product.d.ts.map +1 -0
- package/dist/presets/product.js +74 -0
- package/package.json +91 -0
- package/src/framework/README.md +329 -0
- package/src/framework/__tests__/branding/css-vars.test.ts +147 -0
- package/src/framework/__tests__/components/ErrorBoundary.test.tsx +146 -0
- package/src/framework/__tests__/config/defineConfig.test.ts +138 -0
- package/src/framework/__tests__/hooks/useMotion.test.ts +105 -0
- package/src/framework/__tests__/seo/geo/generateGEOMetadata.test.ts +207 -0
- package/src/framework/__tests__/seo/geo/structuredData.test.ts +262 -0
- package/src/framework/a11y/components/LiveRegion.tsx +89 -0
- package/src/framework/a11y/components/SkipToContent.tsx +103 -0
- package/src/framework/a11y/hooks/useFocusManagement.ts +125 -0
- package/src/framework/a11y/hooks/useFocusTrap.ts +239 -0
- package/src/framework/a11y/hooks/useLiveRegion.ts +95 -0
- package/src/framework/a11y/index.ts +17 -0
- package/src/framework/branding/context.tsx +135 -0
- package/src/framework/branding/css-vars.ts +110 -0
- package/src/framework/branding/tailwind-config.ts +90 -0
- package/src/framework/components/BrandedButton.tsx +94 -0
- package/src/framework/components/BrandedCard.tsx +87 -0
- package/src/framework/components/ErrorBoundary.tsx +215 -0
- package/src/framework/components/HuaUxLayout.tsx +36 -0
- package/src/framework/components/HuaUxPage.tsx +138 -0
- package/src/framework/components/Providers.tsx +98 -0
- package/src/framework/components/WelcomePage.tsx +207 -0
- package/src/framework/config/index.ts +349 -0
- package/src/framework/config/merge.ts +190 -0
- package/src/framework/config/schema.ts +140 -0
- package/src/framework/hooks/useMotion.ts +57 -0
- package/src/framework/index.ts +122 -0
- package/src/framework/license/errors.ts +63 -0
- package/src/framework/license/index.ts +137 -0
- package/src/framework/license/loader.ts +158 -0
- package/src/framework/license/types.ts +95 -0
- package/src/framework/loading/components/SkeletonGroup.tsx +70 -0
- package/src/framework/loading/components/SuspenseWrapper.tsx +88 -0
- package/src/framework/loading/hoc/withSuspense.tsx +96 -0
- package/src/framework/loading/hooks/useDelayedLoading.ts +127 -0
- package/src/framework/loading/hooks/useLoadingState.ts +103 -0
- package/src/framework/loading/index.ts +19 -0
- package/src/framework/middleware/i18n.ts +161 -0
- package/src/framework/middleware/index.ts +7 -0
- package/src/framework/plugins/index.ts +13 -0
- package/src/framework/plugins/registry.ts +186 -0
- package/src/framework/plugins/types.ts +106 -0
- package/src/framework/seo/geo/examples.tsx +415 -0
- package/src/framework/seo/geo/generateGEOMetadata.ts +441 -0
- package/src/framework/seo/geo/index.ts +61 -0
- package/src/framework/seo/geo/presets.ts +58 -0
- package/src/framework/seo/geo/structuredData.ts +422 -0
- package/src/framework/seo/geo/test-utils.ts +179 -0
- package/src/framework/seo/geo/types.ts +315 -0
- package/src/framework/types/index.ts +623 -0
- package/src/framework/utils/data-fetching.ts +95 -0
- package/src/framework/utils/file-structure.ts +88 -0
- package/src/framework/utils/metadata.ts +152 -0
- package/src/index.ts +31 -0
- package/src/presets/index.ts +8 -0
- package/src/presets/marketing.ts +88 -0
- package/src/presets/product.ts +81 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hua-labs/hua-ux/framework - File Structure Validation
|
|
3
|
+
*
|
|
4
|
+
* Validates project file structure according to framework conventions
|
|
5
|
+
*
|
|
6
|
+
* **서버 전용**: 이 모듈은 Node.js 환경에서만 사용 가능합니다.
|
|
7
|
+
* **Server Only**: This module is only available in Node.js environment.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// 서버 환경에서만 fs 사용
|
|
11
|
+
let existsSync: typeof import('fs')['existsSync'];
|
|
12
|
+
let join: typeof import('path')['join'];
|
|
13
|
+
|
|
14
|
+
if (typeof window === 'undefined' && typeof require !== 'undefined') {
|
|
15
|
+
// Node.js 환경
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
existsSync = fs.existsSync;
|
|
19
|
+
join = path.join;
|
|
20
|
+
} else {
|
|
21
|
+
// 클라이언트 환경: 더미 함수 (사용 시 에러)
|
|
22
|
+
existsSync = () => {
|
|
23
|
+
throw new Error('validateFileStructure is only available in Node.js environment');
|
|
24
|
+
};
|
|
25
|
+
join = (...args: string[]) => args.join('/');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Expected directory structure
|
|
30
|
+
*/
|
|
31
|
+
const REQUIRED_DIRS = [
|
|
32
|
+
'app',
|
|
33
|
+
'components',
|
|
34
|
+
'lib',
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
const OPTIONAL_DIRS = [
|
|
38
|
+
'hooks',
|
|
39
|
+
'store',
|
|
40
|
+
'translations',
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* File structure validation result
|
|
45
|
+
*/
|
|
46
|
+
export interface FileStructureResult {
|
|
47
|
+
valid: boolean;
|
|
48
|
+
missing: string[];
|
|
49
|
+
warnings: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Validate project file structure
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const result = validateFileStructure(process.cwd());
|
|
58
|
+
* if (!result.valid) {
|
|
59
|
+
* console.error('Missing directories:', result.missing);
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function validateFileStructure(projectRoot: string): FileStructureResult {
|
|
64
|
+
const missing: string[] = [];
|
|
65
|
+
const warnings: string[] = [];
|
|
66
|
+
|
|
67
|
+
// Check required directories
|
|
68
|
+
for (const dir of REQUIRED_DIRS) {
|
|
69
|
+
const dirPath = join(projectRoot, dir);
|
|
70
|
+
if (!existsSync(dirPath)) {
|
|
71
|
+
missing.push(dir);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check optional directories (warnings only)
|
|
76
|
+
for (const dir of OPTIONAL_DIRS) {
|
|
77
|
+
const dirPath = join(projectRoot, dir);
|
|
78
|
+
if (!existsSync(dirPath)) {
|
|
79
|
+
warnings.push(`Optional directory "${dir}" not found`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
valid: missing.length === 0,
|
|
85
|
+
missing,
|
|
86
|
+
warnings,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hua-labs/hua-ux/framework - Metadata Utilities
|
|
3
|
+
*
|
|
4
|
+
* Next.js Metadata 생성 유틸리티 (Next.js 없이도 사용 가능)
|
|
5
|
+
*
|
|
6
|
+
* Next.js가 설치되어 있으면 Next.js Metadata 타입을 사용하고,
|
|
7
|
+
* 없으면 일반 메타데이터 객체 타입을 사용합니다.
|
|
8
|
+
*
|
|
9
|
+
* Works with or without Next.js:
|
|
10
|
+
* - With Next.js: Returns Next.js Metadata type
|
|
11
|
+
* - Without Next.js: Returns generic metadata object type
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Next.js Metadata 타입 - 조건부 타입으로 타입 안전성 향상
|
|
15
|
+
// Next.js Metadata type - Improved type safety with conditional type
|
|
16
|
+
type MetadataType =
|
|
17
|
+
// Next.js가 설치되어 있으면 실제 Metadata 타입 사용
|
|
18
|
+
// Use actual Metadata type if Next.js is installed
|
|
19
|
+
typeof import('next') extends { Metadata: infer T } ? T
|
|
20
|
+
// 없으면 일반 메타데이터 객체 타입 사용
|
|
21
|
+
// Use generic metadata object type if not installed
|
|
22
|
+
: {
|
|
23
|
+
title?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
keywords?: string[];
|
|
26
|
+
openGraph?: {
|
|
27
|
+
title?: string;
|
|
28
|
+
description?: string;
|
|
29
|
+
type?: 'website' | 'article' | 'product';
|
|
30
|
+
images?: Array<{ url: string }>;
|
|
31
|
+
};
|
|
32
|
+
twitter?: {
|
|
33
|
+
card?: 'summary' | 'summary_large_image';
|
|
34
|
+
title?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
images?: string[];
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* SEO 설정 타입
|
|
42
|
+
*/
|
|
43
|
+
export interface SEOConfig {
|
|
44
|
+
keywords?: string[];
|
|
45
|
+
ogImage?: string;
|
|
46
|
+
ogTitle?: string;
|
|
47
|
+
ogDescription?: string;
|
|
48
|
+
ogType?: 'website' | 'article' | 'product';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 페이지 메타데이터 생성
|
|
53
|
+
*
|
|
54
|
+
* Next.js App Router의 `export const metadata`에서 사용할 수 있는 메타데이터를 생성합니다.
|
|
55
|
+
* CRA/Vite 등 일반 React 앱에서도 사용 가능합니다 (React Helmet 등과 함께).
|
|
56
|
+
*
|
|
57
|
+
* Generates metadata that can be used in Next.js App Router's `export const metadata`.
|
|
58
|
+
* Also works in plain React apps (CRA/Vite) with React Helmet, etc.
|
|
59
|
+
*
|
|
60
|
+
* @param options - 메타데이터 옵션
|
|
61
|
+
* @param options.title - 페이지 제목
|
|
62
|
+
* @param options.description - 페이지 설명
|
|
63
|
+
* @param options.seo - SEO 설정 (선택적)
|
|
64
|
+
* @returns Next.js Metadata 객체 (Next.js가 있으면) 또는 일반 메타데이터 객체 (없으면)
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```tsx
|
|
68
|
+
* // Next.js App Router
|
|
69
|
+
* // app/page.tsx
|
|
70
|
+
* import { generatePageMetadata } from '@hua-labs/hua-ux/framework';
|
|
71
|
+
*
|
|
72
|
+
* export const metadata = generatePageMetadata({
|
|
73
|
+
* title: '홈',
|
|
74
|
+
* description: '환영합니다',
|
|
75
|
+
* seo: {
|
|
76
|
+
* keywords: ['키워드1', '키워드2'],
|
|
77
|
+
* ogImage: '/og-image.png',
|
|
78
|
+
* },
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* export default function HomePage() {
|
|
82
|
+
* return <div>...</div>;
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```tsx
|
|
88
|
+
* // CRA/Vite (React Helmet 사용)
|
|
89
|
+
* import { Helmet } from 'react-helmet-async';
|
|
90
|
+
* import { generatePageMetadata } from '@hua-labs/hua-ux/framework';
|
|
91
|
+
*
|
|
92
|
+
* export default function HomePage() {
|
|
93
|
+
* const metadata = generatePageMetadata({
|
|
94
|
+
* title: 'Home',
|
|
95
|
+
* description: 'Welcome',
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* return (
|
|
99
|
+
* <>
|
|
100
|
+
* <Helmet>
|
|
101
|
+
* <title>{metadata.title}</title>
|
|
102
|
+
* <meta name="description" content={metadata.description} />
|
|
103
|
+
* </Helmet>
|
|
104
|
+
* <div>...</div>
|
|
105
|
+
* </>
|
|
106
|
+
* );
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
export function generatePageMetadata(options: {
|
|
111
|
+
title: string;
|
|
112
|
+
description?: string;
|
|
113
|
+
seo?: SEOConfig;
|
|
114
|
+
}): MetadataType {
|
|
115
|
+
const { title, description, seo } = options;
|
|
116
|
+
|
|
117
|
+
// 메타데이터 객체 직접 구성 (타입 안전하게)
|
|
118
|
+
// Build metadata object directly (type-safe)
|
|
119
|
+
const metadata: Record<string, unknown> = {
|
|
120
|
+
title,
|
|
121
|
+
description,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Keywords 추가
|
|
125
|
+
if (seo?.keywords && seo.keywords.length > 0) {
|
|
126
|
+
metadata.keywords = seo.keywords;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Open Graph 메타데이터
|
|
130
|
+
if (seo?.ogImage || seo?.ogTitle || seo?.ogDescription || seo?.ogType) {
|
|
131
|
+
metadata.openGraph = {
|
|
132
|
+
title: seo.ogTitle || title,
|
|
133
|
+
description: seo.ogDescription || description,
|
|
134
|
+
type: seo.ogType || 'website',
|
|
135
|
+
...(seo.ogImage && { images: [{ url: seo.ogImage }] }),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Twitter Card 메타데이터 (Open Graph가 있으면 자동으로 사용)
|
|
140
|
+
if (seo?.ogImage) {
|
|
141
|
+
metadata.twitter = {
|
|
142
|
+
card: 'summary_large_image',
|
|
143
|
+
...(seo.ogTitle && { title: seo.ogTitle }),
|
|
144
|
+
...(seo.ogDescription && { description: seo.ogDescription }),
|
|
145
|
+
...(seo.ogImage && { images: [seo.ogImage] }),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// MetadataType으로 타입 단언 (조건부 타입이 컴파일 타임에 올바르게 추론됨)
|
|
150
|
+
// Type assertion to MetadataType (conditional type is correctly inferred at compile time)
|
|
151
|
+
return metadata as MetadataType;
|
|
152
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hua-labs/hua-ux
|
|
3
|
+
*
|
|
4
|
+
* Ship UX faster: UI + motion + i18n, pre-wired.
|
|
5
|
+
* A framework for React product teams.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Re-export UI components
|
|
9
|
+
// Note: Button and Card are overridden below with branded versions
|
|
10
|
+
export * from '@hua-labs/ui'
|
|
11
|
+
|
|
12
|
+
// Override Button and Card with branded versions for automatic branding
|
|
13
|
+
// When branding is configured, these components automatically use branding colors
|
|
14
|
+
// When branding is not configured, they work exactly like the original components
|
|
15
|
+
export { BrandedButton as Button, BrandedCard as Card } from './framework'
|
|
16
|
+
|
|
17
|
+
// Re-export Button and Card types (they're compatible with BrandedButton and BrandedCard)
|
|
18
|
+
export type { ButtonProps } from '@hua-labs/ui'
|
|
19
|
+
export type { CardProps, CardHeaderProps, CardTitleProps, CardDescriptionProps, CardContentProps, CardFooterProps } from '@hua-labs/ui'
|
|
20
|
+
|
|
21
|
+
// Re-export Motion hooks
|
|
22
|
+
export * from '@hua-labs/motion-core'
|
|
23
|
+
|
|
24
|
+
// Re-export i18n core
|
|
25
|
+
export * from '@hua-labs/i18n-core'
|
|
26
|
+
|
|
27
|
+
// Re-export i18n Zustand adapter
|
|
28
|
+
export * from '@hua-labs/i18n-core-zustand'
|
|
29
|
+
|
|
30
|
+
// Re-export state management
|
|
31
|
+
export * from '@hua-labs/state'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marketing Preset
|
|
3
|
+
*
|
|
4
|
+
* 랜딩 페이지 모션 중심 설정
|
|
5
|
+
* 마케팅 페이지, 랜딩 페이지에 적합
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MotionPreset, PresetConfig } from '@hua-labs/motion-core'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Marketing Preset Configuration
|
|
12
|
+
*
|
|
13
|
+
* - 드라마틱한 모션 (느린 전환, 긴 딜레이)
|
|
14
|
+
* - 넓은 스페이싱 (xl 기본값)
|
|
15
|
+
* - 호버/클릭 인터랙션 강조
|
|
16
|
+
*/
|
|
17
|
+
export const marketingPreset = {
|
|
18
|
+
/**
|
|
19
|
+
* Motion Presets for Marketing UI
|
|
20
|
+
*/
|
|
21
|
+
motion: {
|
|
22
|
+
hero: {
|
|
23
|
+
entrance: 'fadeIn',
|
|
24
|
+
delay: 300,
|
|
25
|
+
duration: 1000,
|
|
26
|
+
hover: false,
|
|
27
|
+
click: false
|
|
28
|
+
} as MotionPreset,
|
|
29
|
+
title: {
|
|
30
|
+
entrance: 'slideUp',
|
|
31
|
+
delay: 500,
|
|
32
|
+
duration: 800,
|
|
33
|
+
hover: false,
|
|
34
|
+
click: false
|
|
35
|
+
} as MotionPreset,
|
|
36
|
+
button: {
|
|
37
|
+
entrance: 'scaleIn',
|
|
38
|
+
delay: 700,
|
|
39
|
+
duration: 400,
|
|
40
|
+
hover: true,
|
|
41
|
+
click: true
|
|
42
|
+
} as MotionPreset,
|
|
43
|
+
card: {
|
|
44
|
+
entrance: 'slideUp',
|
|
45
|
+
delay: 600,
|
|
46
|
+
duration: 600,
|
|
47
|
+
hover: true,
|
|
48
|
+
click: false
|
|
49
|
+
} as MotionPreset,
|
|
50
|
+
text: {
|
|
51
|
+
entrance: 'fadeIn',
|
|
52
|
+
delay: 400,
|
|
53
|
+
duration: 700,
|
|
54
|
+
hover: false,
|
|
55
|
+
click: false
|
|
56
|
+
} as MotionPreset,
|
|
57
|
+
image: {
|
|
58
|
+
entrance: 'scaleIn',
|
|
59
|
+
delay: 500,
|
|
60
|
+
duration: 800,
|
|
61
|
+
hover: true,
|
|
62
|
+
click: false
|
|
63
|
+
} as MotionPreset
|
|
64
|
+
} as PresetConfig,
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Spacing Guidelines
|
|
68
|
+
*
|
|
69
|
+
* - 기본 스페이싱: xl (24px)
|
|
70
|
+
* - 섹션 간격: xl (32px+)
|
|
71
|
+
* - 컴포넌트 내부: md (8px)
|
|
72
|
+
*/
|
|
73
|
+
spacing: {
|
|
74
|
+
default: 'xl',
|
|
75
|
+
section: 'xl',
|
|
76
|
+
component: 'md'
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* i18n Configuration
|
|
81
|
+
*/
|
|
82
|
+
i18n: {
|
|
83
|
+
defaultLanguage: 'ko',
|
|
84
|
+
supportedLanguages: ['ko', 'en']
|
|
85
|
+
}
|
|
86
|
+
} as const
|
|
87
|
+
|
|
88
|
+
export type MarketingPreset = typeof marketingPreset
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product Preset
|
|
3
|
+
*
|
|
4
|
+
* 기본 여백 + 기본 모션 설정
|
|
5
|
+
* 제품 페이지, 대시보드 등 일반적인 제품 UI에 적합
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MotionPreset, PresetConfig } from '@hua-labs/motion-core'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Product Preset Configuration
|
|
12
|
+
*
|
|
13
|
+
* - 보수적인 모션 (빠른 전환, 최소한의 딜레이)
|
|
14
|
+
* - 일관된 스페이싱 (md 기본값)
|
|
15
|
+
* - 호버/클릭 인터랙션 최소화
|
|
16
|
+
*/
|
|
17
|
+
export const productPreset = {
|
|
18
|
+
/**
|
|
19
|
+
* Motion Presets for Product UI
|
|
20
|
+
*/
|
|
21
|
+
motion: {
|
|
22
|
+
hero: {
|
|
23
|
+
entrance: 'fadeIn',
|
|
24
|
+
delay: 100,
|
|
25
|
+
duration: 400,
|
|
26
|
+
hover: false,
|
|
27
|
+
click: false
|
|
28
|
+
} as MotionPreset,
|
|
29
|
+
title: {
|
|
30
|
+
entrance: 'slideUp',
|
|
31
|
+
delay: 150,
|
|
32
|
+
duration: 350,
|
|
33
|
+
hover: false,
|
|
34
|
+
click: false
|
|
35
|
+
} as MotionPreset,
|
|
36
|
+
button: {
|
|
37
|
+
entrance: 'scaleIn',
|
|
38
|
+
delay: 200,
|
|
39
|
+
duration: 200,
|
|
40
|
+
hover: true,
|
|
41
|
+
click: true
|
|
42
|
+
} as MotionPreset,
|
|
43
|
+
card: {
|
|
44
|
+
entrance: 'slideUp',
|
|
45
|
+
delay: 100,
|
|
46
|
+
duration: 300,
|
|
47
|
+
hover: true,
|
|
48
|
+
click: false
|
|
49
|
+
} as MotionPreset,
|
|
50
|
+
text: {
|
|
51
|
+
entrance: 'fadeIn',
|
|
52
|
+
delay: 50,
|
|
53
|
+
duration: 300,
|
|
54
|
+
hover: false,
|
|
55
|
+
click: false
|
|
56
|
+
} as MotionPreset
|
|
57
|
+
} as PresetConfig,
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Spacing Guidelines
|
|
61
|
+
*
|
|
62
|
+
* - 기본 스페이싱: md (8px)
|
|
63
|
+
* - 섹션 간격: lg (16px)
|
|
64
|
+
* - 컴포넌트 내부: sm (4px)
|
|
65
|
+
*/
|
|
66
|
+
spacing: {
|
|
67
|
+
default: 'md',
|
|
68
|
+
section: 'lg',
|
|
69
|
+
component: 'sm'
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* i18n Configuration
|
|
74
|
+
*/
|
|
75
|
+
i18n: {
|
|
76
|
+
defaultLanguage: 'ko',
|
|
77
|
+
supportedLanguages: ['ko', 'en']
|
|
78
|
+
}
|
|
79
|
+
} as const
|
|
80
|
+
|
|
81
|
+
export type ProductPreset = typeof productPreset
|