@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.
Files changed (210) hide show
  1. package/README.md +839 -0
  2. package/dist/framework/a11y/components/LiveRegion.d.ts +64 -0
  3. package/dist/framework/a11y/components/LiveRegion.d.ts.map +1 -0
  4. package/dist/framework/a11y/components/LiveRegion.js +43 -0
  5. package/dist/framework/a11y/components/SkipToContent.d.ts +62 -0
  6. package/dist/framework/a11y/components/SkipToContent.d.ts.map +1 -0
  7. package/dist/framework/a11y/components/SkipToContent.js +60 -0
  8. package/dist/framework/a11y/hooks/useFocusManagement.d.ts +60 -0
  9. package/dist/framework/a11y/hooks/useFocusManagement.d.ts.map +1 -0
  10. package/dist/framework/a11y/hooks/useFocusManagement.js +71 -0
  11. package/dist/framework/a11y/hooks/useFocusTrap.d.ts +64 -0
  12. package/dist/framework/a11y/hooks/useFocusTrap.d.ts.map +1 -0
  13. package/dist/framework/a11y/hooks/useFocusTrap.js +185 -0
  14. package/dist/framework/a11y/hooks/useLiveRegion.d.ts +56 -0
  15. package/dist/framework/a11y/hooks/useLiveRegion.d.ts.map +1 -0
  16. package/dist/framework/a11y/hooks/useLiveRegion.js +60 -0
  17. package/dist/framework/a11y/index.d.ts +16 -0
  18. package/dist/framework/a11y/index.d.ts.map +1 -0
  19. package/dist/framework/a11y/index.js +11 -0
  20. package/dist/framework/branding/context.d.ts +52 -0
  21. package/dist/framework/branding/context.d.ts.map +1 -0
  22. package/dist/framework/branding/context.js +96 -0
  23. package/dist/framework/branding/css-vars.d.ts +34 -0
  24. package/dist/framework/branding/css-vars.d.ts.map +1 -0
  25. package/dist/framework/branding/css-vars.js +95 -0
  26. package/dist/framework/branding/tailwind-config.d.ts +38 -0
  27. package/dist/framework/branding/tailwind-config.d.ts.map +1 -0
  28. package/dist/framework/branding/tailwind-config.js +66 -0
  29. package/dist/framework/components/BrandedButton.d.ts +53 -0
  30. package/dist/framework/components/BrandedButton.d.ts.map +1 -0
  31. package/dist/framework/components/BrandedButton.js +40 -0
  32. package/dist/framework/components/BrandedCard.d.ts +52 -0
  33. package/dist/framework/components/BrandedCard.d.ts.map +1 -0
  34. package/dist/framework/components/BrandedCard.js +73 -0
  35. package/dist/framework/components/ErrorBoundary.d.ts +92 -0
  36. package/dist/framework/components/ErrorBoundary.d.ts.map +1 -0
  37. package/dist/framework/components/ErrorBoundary.js +121 -0
  38. package/dist/framework/components/HuaUxLayout.d.ts +29 -0
  39. package/dist/framework/components/HuaUxLayout.d.ts.map +1 -0
  40. package/dist/framework/components/HuaUxLayout.js +32 -0
  41. package/dist/framework/components/HuaUxPage.d.ts +48 -0
  42. package/dist/framework/components/HuaUxPage.d.ts.map +1 -0
  43. package/dist/framework/components/HuaUxPage.js +105 -0
  44. package/dist/framework/components/Providers.d.ts +17 -0
  45. package/dist/framework/components/Providers.d.ts.map +1 -0
  46. package/dist/framework/components/Providers.js +72 -0
  47. package/dist/framework/components/WelcomePage.d.ts +44 -0
  48. package/dist/framework/components/WelcomePage.d.ts.map +1 -0
  49. package/dist/framework/components/WelcomePage.js +80 -0
  50. package/dist/framework/config/index.d.ts +182 -0
  51. package/dist/framework/config/index.d.ts.map +1 -0
  52. package/dist/framework/config/index.js +329 -0
  53. package/dist/framework/config/merge.d.ts +26 -0
  54. package/dist/framework/config/merge.d.ts.map +1 -0
  55. package/dist/framework/config/merge.js +160 -0
  56. package/dist/framework/config/schema.d.ts +25 -0
  57. package/dist/framework/config/schema.d.ts.map +1 -0
  58. package/dist/framework/config/schema.js +122 -0
  59. package/dist/framework/hooks/useMotion.d.ts +45 -0
  60. package/dist/framework/hooks/useMotion.d.ts.map +1 -0
  61. package/dist/framework/hooks/useMotion.js +40 -0
  62. package/dist/framework/index.d.ts +37 -0
  63. package/dist/framework/index.d.ts.map +1 -0
  64. package/dist/framework/index.js +42 -0
  65. package/dist/framework/license/errors.d.ts +15 -0
  66. package/dist/framework/license/errors.d.ts.map +1 -0
  67. package/dist/framework/license/errors.js +52 -0
  68. package/dist/framework/license/index.d.ts +70 -0
  69. package/dist/framework/license/index.d.ts.map +1 -0
  70. package/dist/framework/license/index.js +124 -0
  71. package/dist/framework/license/loader.d.ts +26 -0
  72. package/dist/framework/license/loader.d.ts.map +1 -0
  73. package/dist/framework/license/loader.js +137 -0
  74. package/dist/framework/license/types.d.ts +67 -0
  75. package/dist/framework/license/types.d.ts.map +1 -0
  76. package/dist/framework/license/types.js +18 -0
  77. package/dist/framework/loading/components/SkeletonGroup.d.ts +44 -0
  78. package/dist/framework/loading/components/SkeletonGroup.d.ts.map +1 -0
  79. package/dist/framework/loading/components/SkeletonGroup.js +34 -0
  80. package/dist/framework/loading/components/SuspenseWrapper.d.ts +58 -0
  81. package/dist/framework/loading/components/SuspenseWrapper.d.ts.map +1 -0
  82. package/dist/framework/loading/components/SuspenseWrapper.js +40 -0
  83. package/dist/framework/loading/hoc/withSuspense.d.ts +46 -0
  84. package/dist/framework/loading/hoc/withSuspense.d.ts.map +1 -0
  85. package/dist/framework/loading/hoc/withSuspense.js +54 -0
  86. package/dist/framework/loading/hooks/useDelayedLoading.d.ts +56 -0
  87. package/dist/framework/loading/hooks/useDelayedLoading.d.ts.map +1 -0
  88. package/dist/framework/loading/hooks/useDelayedLoading.js +97 -0
  89. package/dist/framework/loading/hooks/useLoadingState.d.ts +69 -0
  90. package/dist/framework/loading/hooks/useLoadingState.d.ts.map +1 -0
  91. package/dist/framework/loading/hooks/useLoadingState.js +59 -0
  92. package/dist/framework/loading/index.d.ts +16 -0
  93. package/dist/framework/loading/index.d.ts.map +1 -0
  94. package/dist/framework/loading/index.js +13 -0
  95. package/dist/framework/middleware/i18n.d.ts +90 -0
  96. package/dist/framework/middleware/i18n.d.ts.map +1 -0
  97. package/dist/framework/middleware/i18n.js +99 -0
  98. package/dist/framework/plugins/index.d.ts +8 -0
  99. package/dist/framework/plugins/index.d.ts.map +1 -0
  100. package/dist/framework/plugins/index.js +6 -0
  101. package/dist/framework/plugins/registry.d.ts +95 -0
  102. package/dist/framework/plugins/registry.d.ts.map +1 -0
  103. package/dist/framework/plugins/registry.js +160 -0
  104. package/dist/framework/plugins/types.d.ts +97 -0
  105. package/dist/framework/plugins/types.d.ts.map +1 -0
  106. package/dist/framework/plugins/types.js +6 -0
  107. package/dist/framework/seo/geo/examples.d.ts +87 -0
  108. package/dist/framework/seo/geo/examples.d.ts.map +1 -0
  109. package/dist/framework/seo/geo/examples.js +295 -0
  110. package/dist/framework/seo/geo/generateGEOMetadata.d.ts +107 -0
  111. package/dist/framework/seo/geo/generateGEOMetadata.d.ts.map +1 -0
  112. package/dist/framework/seo/geo/generateGEOMetadata.js +404 -0
  113. package/dist/framework/seo/geo/index.d.ts +19 -0
  114. package/dist/framework/seo/geo/index.d.ts.map +1 -0
  115. package/dist/framework/seo/geo/index.js +21 -0
  116. package/dist/framework/seo/geo/presets.d.ts +52 -0
  117. package/dist/framework/seo/geo/presets.d.ts.map +1 -0
  118. package/dist/framework/seo/geo/presets.js +47 -0
  119. package/dist/framework/seo/geo/structuredData.d.ts +187 -0
  120. package/dist/framework/seo/geo/structuredData.d.ts.map +1 -0
  121. package/dist/framework/seo/geo/structuredData.js +354 -0
  122. package/dist/framework/seo/geo/test-utils.d.ts +78 -0
  123. package/dist/framework/seo/geo/test-utils.d.ts.map +1 -0
  124. package/dist/framework/seo/geo/test-utils.js +139 -0
  125. package/dist/framework/seo/geo/types.d.ts +225 -0
  126. package/dist/framework/seo/geo/types.d.ts.map +1 -0
  127. package/dist/framework/seo/geo/types.js +51 -0
  128. package/dist/framework/types/index.d.ts +577 -0
  129. package/dist/framework/types/index.d.ts.map +1 -0
  130. package/dist/framework/types/index.js +6 -0
  131. package/dist/framework/utils/data-fetching.d.ts +45 -0
  132. package/dist/framework/utils/data-fetching.d.ts.map +1 -0
  133. package/dist/framework/utils/data-fetching.js +74 -0
  134. package/dist/framework/utils/file-structure.d.ts +29 -0
  135. package/dist/framework/utils/file-structure.d.ts.map +1 -0
  136. package/dist/framework/utils/file-structure.js +72 -0
  137. package/dist/framework/utils/metadata.d.ts +109 -0
  138. package/dist/framework/utils/metadata.d.ts.map +1 -0
  139. package/dist/framework/utils/metadata.js +105 -0
  140. package/dist/index.d.ts +15 -0
  141. package/dist/index.d.ts.map +1 -0
  142. package/dist/index.js +21 -0
  143. package/dist/presets/index.d.ts +8 -0
  144. package/dist/presets/index.d.ts.map +1 -0
  145. package/dist/presets/index.js +7 -0
  146. package/dist/presets/marketing.d.ts +41 -0
  147. package/dist/presets/marketing.d.ts.map +1 -0
  148. package/dist/presets/marketing.js +81 -0
  149. package/dist/presets/product.d.ts +41 -0
  150. package/dist/presets/product.d.ts.map +1 -0
  151. package/dist/presets/product.js +74 -0
  152. package/package.json +91 -0
  153. package/src/framework/README.md +329 -0
  154. package/src/framework/__tests__/branding/css-vars.test.ts +147 -0
  155. package/src/framework/__tests__/components/ErrorBoundary.test.tsx +146 -0
  156. package/src/framework/__tests__/config/defineConfig.test.ts +138 -0
  157. package/src/framework/__tests__/hooks/useMotion.test.ts +105 -0
  158. package/src/framework/__tests__/seo/geo/generateGEOMetadata.test.ts +207 -0
  159. package/src/framework/__tests__/seo/geo/structuredData.test.ts +262 -0
  160. package/src/framework/a11y/components/LiveRegion.tsx +89 -0
  161. package/src/framework/a11y/components/SkipToContent.tsx +103 -0
  162. package/src/framework/a11y/hooks/useFocusManagement.ts +125 -0
  163. package/src/framework/a11y/hooks/useFocusTrap.ts +239 -0
  164. package/src/framework/a11y/hooks/useLiveRegion.ts +95 -0
  165. package/src/framework/a11y/index.ts +17 -0
  166. package/src/framework/branding/context.tsx +135 -0
  167. package/src/framework/branding/css-vars.ts +110 -0
  168. package/src/framework/branding/tailwind-config.ts +90 -0
  169. package/src/framework/components/BrandedButton.tsx +94 -0
  170. package/src/framework/components/BrandedCard.tsx +87 -0
  171. package/src/framework/components/ErrorBoundary.tsx +215 -0
  172. package/src/framework/components/HuaUxLayout.tsx +36 -0
  173. package/src/framework/components/HuaUxPage.tsx +138 -0
  174. package/src/framework/components/Providers.tsx +98 -0
  175. package/src/framework/components/WelcomePage.tsx +207 -0
  176. package/src/framework/config/index.ts +349 -0
  177. package/src/framework/config/merge.ts +190 -0
  178. package/src/framework/config/schema.ts +140 -0
  179. package/src/framework/hooks/useMotion.ts +57 -0
  180. package/src/framework/index.ts +122 -0
  181. package/src/framework/license/errors.ts +63 -0
  182. package/src/framework/license/index.ts +137 -0
  183. package/src/framework/license/loader.ts +158 -0
  184. package/src/framework/license/types.ts +95 -0
  185. package/src/framework/loading/components/SkeletonGroup.tsx +70 -0
  186. package/src/framework/loading/components/SuspenseWrapper.tsx +88 -0
  187. package/src/framework/loading/hoc/withSuspense.tsx +96 -0
  188. package/src/framework/loading/hooks/useDelayedLoading.ts +127 -0
  189. package/src/framework/loading/hooks/useLoadingState.ts +103 -0
  190. package/src/framework/loading/index.ts +19 -0
  191. package/src/framework/middleware/i18n.ts +161 -0
  192. package/src/framework/middleware/index.ts +7 -0
  193. package/src/framework/plugins/index.ts +13 -0
  194. package/src/framework/plugins/registry.ts +186 -0
  195. package/src/framework/plugins/types.ts +106 -0
  196. package/src/framework/seo/geo/examples.tsx +415 -0
  197. package/src/framework/seo/geo/generateGEOMetadata.ts +441 -0
  198. package/src/framework/seo/geo/index.ts +61 -0
  199. package/src/framework/seo/geo/presets.ts +58 -0
  200. package/src/framework/seo/geo/structuredData.ts +422 -0
  201. package/src/framework/seo/geo/test-utils.ts +179 -0
  202. package/src/framework/seo/geo/types.ts +315 -0
  203. package/src/framework/types/index.ts +623 -0
  204. package/src/framework/utils/data-fetching.ts +95 -0
  205. package/src/framework/utils/file-structure.ts +88 -0
  206. package/src/framework/utils/metadata.ts +152 -0
  207. package/src/index.ts +31 -0
  208. package/src/presets/index.ts +8 -0
  209. package/src/presets/marketing.ts +88 -0
  210. package/src/presets/product.ts +81 -0
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - Config Merge
3
+ *
4
+ * Preset 병합 및 깊은 병합 로직
5
+ */
6
+
7
+ import type { HuaUxConfig, Preset, PresetName, PresetConfig } from '../types';
8
+ import { productPreset } from '../../presets/product';
9
+ import { marketingPreset } from '../../presets/marketing';
10
+
11
+ /**
12
+ * Preset 맵
13
+ */
14
+ const PRESET_MAP: Record<PresetName, any> = {
15
+ product: productPreset,
16
+ marketing: marketingPreset,
17
+ };
18
+
19
+ /**
20
+ * 깊은 병합 (Deep Merge)
21
+ *
22
+ * 중첩된 객체를 재귀적으로 병합합니다.
23
+ * 사용자 설정이 Preset 설정보다 우선합니다.
24
+ */
25
+ function deepMerge<T extends Record<string, any>>(
26
+ target: T,
27
+ source: Partial<T>
28
+ ): T {
29
+ const result = { ...target };
30
+
31
+ for (const key in source) {
32
+ if (source[key] === undefined) {
33
+ // undefined는 병합에서 제외 (사용자가 명시적으로 끄려는 경우)
34
+ continue;
35
+ }
36
+
37
+ if (
38
+ source[key] &&
39
+ typeof source[key] === 'object' &&
40
+ !Array.isArray(source[key]) &&
41
+ target[key] &&
42
+ typeof target[key] === 'object' &&
43
+ !Array.isArray(target[key])
44
+ ) {
45
+ // 중첩 객체는 재귀적으로 병합
46
+ result[key] = deepMerge(target[key], source[key] as Partial<T[Extract<keyof T, string>]>);
47
+ } else {
48
+ // 배열이나 원시값은 그대로 덮어쓰기
49
+ result[key] = source[key] as T[Extract<keyof T, string>];
50
+ }
51
+ }
52
+
53
+ return result;
54
+ }
55
+
56
+ /**
57
+ * Preset에서 Config로 변환
58
+ *
59
+ * Preset의 구조를 HuaUxConfig 형식으로 변환합니다.
60
+ */
61
+ function presetToConfig(preset: typeof productPreset | typeof marketingPreset): Partial<HuaUxConfig> {
62
+ return {
63
+ motion: {
64
+ defaultPreset: preset === productPreset ? 'product' : 'marketing',
65
+ enableAnimations: true,
66
+ },
67
+ i18n: {
68
+ defaultLanguage: preset.i18n.defaultLanguage,
69
+ supportedLanguages: [...preset.i18n.supportedLanguages],
70
+ },
71
+ // Preset의 spacing은 나중에 컴포넌트에서 사용
72
+ // Config에는 직접 포함하지 않음 (컴포넌트가 PresetContext에서 가져옴)
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Preset과 사용자 설정 병합
78
+ *
79
+ * 1. Preset 기본값 로드
80
+ * 2. Preset 설정 오버라이드 (개발자 모드인 경우)
81
+ * 3. 사용자 설정으로 병합 (사용자 설정 우선)
82
+ * 4. 최종 검증
83
+ *
84
+ * @param preset - 사용할 Preset (문자열 또는 객체)
85
+ * @param userConfig - 사용자 설정 (선택적)
86
+ * @returns 병합된 설정
87
+ */
88
+ export function mergePresetWithConfig(
89
+ preset: Preset,
90
+ userConfig?: Partial<HuaUxConfig>
91
+ ): HuaUxConfig {
92
+ // 1. Preset 타입 추출
93
+ let presetName: PresetName;
94
+ let presetOverrides: Partial<HuaUxConfig> = {};
95
+
96
+ if (typeof preset === 'string') {
97
+ // 바이브 모드: 문자열 Preset
98
+ presetName = preset;
99
+ } else {
100
+ // 개발자 모드: 객체 Preset
101
+ presetName = preset.type;
102
+
103
+ // Preset 설정 오버라이드
104
+ if (preset.motion) {
105
+ presetOverrides.motion = {
106
+ ...presetOverrides.motion,
107
+ duration: preset.motion.duration,
108
+ easing: preset.motion.easing,
109
+ };
110
+ }
111
+
112
+ // spacing은 나중에 컴포넌트 레벨에서 처리
113
+ // (Config에는 직접 포함하지 않음)
114
+ }
115
+
116
+ // 2. Preset 로드
117
+ const presetData = PRESET_MAP[presetName];
118
+ if (!presetData) {
119
+ throw new Error(
120
+ `Unknown preset: "${presetName}". Available presets: ${Object.keys(PRESET_MAP).join(', ')}`
121
+ );
122
+ }
123
+
124
+ // 3. Preset을 Config 형식으로 변환
125
+ const presetConfig = presetToConfig(presetData);
126
+
127
+ // preset 속성 추가 (원본 preset 정보 보존)
128
+ presetConfig.preset = presetName;
129
+
130
+ // 4. Preset 오버라이드와 병합
131
+ const presetWithOverrides = deepMerge(presetConfig, presetOverrides);
132
+
133
+ // 5. 사용자 설정과 병합 (사용자 설정 우선)
134
+ const merged = userConfig
135
+ ? deepMerge(presetWithOverrides, userConfig)
136
+ : presetWithOverrides;
137
+
138
+ // 4. 최종 Config 형식으로 변환 (필수 필드 보장)
139
+ return {
140
+ ...presetConfig,
141
+ ...merged,
142
+ // i18n은 항상 있어야 함 (Preset에서 제공)
143
+ i18n: merged.i18n || presetConfig.i18n,
144
+ // motion은 항상 있어야 함
145
+ motion: merged.motion || presetConfig.motion,
146
+ // state는 기본값 사용
147
+ state: merged.state || {
148
+ persist: true,
149
+ ssr: true,
150
+ },
151
+ // fileStructure는 기본값 사용
152
+ fileStructure: merged.fileStructure || {
153
+ enforce: false,
154
+ },
155
+ } as HuaUxConfig;
156
+ }
157
+
158
+ /**
159
+ * Preset 없이 사용자 설정만으로 Config 생성
160
+ *
161
+ * Preset을 사용하지 않고 모든 설정을 직접 지정하는 경우
162
+ */
163
+ export function createConfigFromUserConfig(
164
+ userConfig: Partial<HuaUxConfig>
165
+ ): HuaUxConfig {
166
+ // 기본값과 사용자 설정 병합
167
+ const defaultConfig: Partial<HuaUxConfig> = {
168
+ i18n: {
169
+ defaultLanguage: 'ko',
170
+ supportedLanguages: ['ko', 'en'],
171
+ fallbackLanguage: 'en',
172
+ namespaces: ['common'],
173
+ translationLoader: 'api',
174
+ translationApiPath: '/api/translations',
175
+ },
176
+ motion: {
177
+ defaultPreset: 'product',
178
+ enableAnimations: true,
179
+ },
180
+ state: {
181
+ persist: true,
182
+ ssr: true,
183
+ },
184
+ fileStructure: {
185
+ enforce: false,
186
+ },
187
+ };
188
+
189
+ return deepMerge(defaultConfig, userConfig) as HuaUxConfig;
190
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - Config Schema
3
+ *
4
+ * Configuration schema and validation
5
+ */
6
+
7
+ import type { HuaUxConfig, Preset } from '../types';
8
+
9
+ /**
10
+ * Default configuration
11
+ *
12
+ * Preset을 사용하지 않을 때의 기본값입니다.
13
+ * Preset을 사용하면 이 값은 무시되고 Preset 값이 사용됩니다.
14
+ */
15
+ export const defaultConfig: Required<Omit<HuaUxConfig, 'branding' | 'plugins' | 'license'>> & {
16
+ branding?: HuaUxConfig['branding'];
17
+ plugins?: HuaUxConfig['plugins'];
18
+ license?: HuaUxConfig['license'];
19
+ } = {
20
+ preset: 'product', // 기본 Preset
21
+ i18n: {
22
+ defaultLanguage: 'ko',
23
+ supportedLanguages: ['ko', 'en'],
24
+ fallbackLanguage: 'en',
25
+ namespaces: ['common'],
26
+ translationLoader: 'api',
27
+ translationApiPath: '/api/translations',
28
+ },
29
+ motion: {
30
+ defaultPreset: 'product',
31
+ enableAnimations: true,
32
+ },
33
+ state: {
34
+ persist: true,
35
+ ssr: true,
36
+ },
37
+ fileStructure: {
38
+ enforce: false,
39
+ },
40
+ plugins: [],
41
+ license: undefined,
42
+ };
43
+
44
+ /**
45
+ * Validate configuration
46
+ *
47
+ * 설정의 유효성을 검증하고 친절한 에러 메시지를 제공합니다.
48
+ * Validates configuration and provides friendly error messages.
49
+ */
50
+ export function validateConfig(config: Partial<HuaUxConfig>): HuaUxConfig {
51
+ // Preset 검증
52
+ if (config.preset) {
53
+ if (typeof config.preset === 'string') {
54
+ // 바이브 모드: 문자열 Preset
55
+ if (!['product', 'marketing'].includes(config.preset)) {
56
+ throw new Error(
57
+ `[hua-ux] ❌ 잘못된 Preset입니다: "${config.preset}"\n` +
58
+ `[hua-ux] ❌ Invalid preset: "${config.preset}"\n\n` +
59
+ `사용 가능한 Preset: 'product', 'marketing'\n` +
60
+ `Available presets: 'product', 'marketing'\n\n` +
61
+ `💡 해결 방법 / Solution:\n` +
62
+ ` - 'product' 또는 'marketing' 중 하나를 선택하세요.\n` +
63
+ ` - Select either 'product' or 'marketing'.\n\n` +
64
+ `📖 가이드 / Guide: https://github.com/HUA-Labs/hua-platform/tree/main/packages/hua-ux/docs`
65
+ );
66
+ }
67
+ } else {
68
+ // 개발자 모드: 객체 Preset
69
+ if (!['product', 'marketing'].includes(config.preset.type)) {
70
+ throw new Error(
71
+ `[hua-ux] ❌ 잘못된 Preset 타입입니다: "${config.preset.type}"\n` +
72
+ `[hua-ux] ❌ Invalid preset type: "${config.preset.type}"\n\n` +
73
+ `사용 가능한 Preset 타입: 'product', 'marketing'\n` +
74
+ `Available preset types: 'product', 'marketing'\n\n` +
75
+ `💡 해결 방법 / Solution:\n` +
76
+ ` - preset.type을 'product' 또는 'marketing'으로 설정하세요.\n` +
77
+ ` - Set preset.type to either 'product' or 'marketing'.\n\n` +
78
+ `📖 가이드 / Guide: https://github.com/HUA-Labs/hua-platform/tree/main/packages/hua-ux/docs`
79
+ );
80
+ }
81
+ }
82
+ }
83
+
84
+ // motion.style을 defaultPreset으로 매핑 (바이브 코더용)
85
+ if (config.motion?.style && !config.motion.defaultPreset) {
86
+ const styleToPreset: Record<string, 'product' | 'marketing'> = {
87
+ smooth: 'product',
88
+ minimal: 'product',
89
+ dramatic: 'marketing',
90
+ };
91
+
92
+ config.motion.defaultPreset = styleToPreset[config.motion.style];
93
+ }
94
+
95
+ // 기본값과 병합
96
+ const validated: HuaUxConfig = {
97
+ preset: config.preset || defaultConfig.preset,
98
+ i18n: {
99
+ ...defaultConfig.i18n,
100
+ ...config.i18n,
101
+ },
102
+ motion: {
103
+ ...defaultConfig.motion,
104
+ ...config.motion,
105
+ },
106
+ state: {
107
+ ...defaultConfig.state,
108
+ ...config.state,
109
+ },
110
+ fileStructure: {
111
+ ...defaultConfig.fileStructure,
112
+ ...config.fileStructure,
113
+ },
114
+ };
115
+
116
+ // Validate i18n
117
+ if (validated.i18n) {
118
+ if (!validated.i18n.supportedLanguages.includes(validated.i18n.defaultLanguage)) {
119
+ throw new Error(
120
+ `[hua-ux] ❌ i18n 설정 오류 / i18n configuration error\n\n` +
121
+ `기본 언어 "${validated.i18n.defaultLanguage}"가 지원 언어 목록에 없습니다.\n` +
122
+ `Default language "${validated.i18n.defaultLanguage}" is not in supportedLanguages.\n\n` +
123
+ `현재 지원 언어 / Current supported languages: ${validated.i18n.supportedLanguages.join(', ')}\n\n` +
124
+ `💡 해결 방법 / Solution:\n` +
125
+ ` 1. supportedLanguages에 "${validated.i18n.defaultLanguage}"를 추가하세요.\n` +
126
+ ` Add "${validated.i18n.defaultLanguage}" to supportedLanguages.\n` +
127
+ ` 2. 또는 defaultLanguage를 지원 언어 중 하나로 변경하세요.\n` +
128
+ ` Or change defaultLanguage to one of the supported languages.\n\n` +
129
+ `📝 예시 / Example:\n` +
130
+ ` i18n: {\n` +
131
+ ` defaultLanguage: 'ko',\n` +
132
+ ` supportedLanguages: ['ko', 'en', 'ja'], // 'ko' 포함 필수\n` +
133
+ ` }\n\n` +
134
+ `📖 가이드 / Guide: https://github.com/HUA-Labs/hua-platform/tree/main/packages/hua-ux/docs`
135
+ );
136
+ }
137
+ }
138
+
139
+ return validated;
140
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - useMotion
3
+ *
4
+ * 통합 Motion Hook - motion-core의 useUnifiedMotion을 래핑
5
+ * Unified Motion Hook - Wraps motion-core's useUnifiedMotion
6
+ *
7
+ * 이 hook은 motion-core의 useUnifiedMotion을 재export하여
8
+ * hua-ux 프레임워크에서 일관된 API를 제공합니다.
9
+ *
10
+ * This hook re-exports motion-core's useUnifiedMotion to provide
11
+ * a consistent API within the hua-ux framework.
12
+ */
13
+
14
+ 'use client';
15
+
16
+ import type { BaseMotionReturn, MotionElement, EntranceType } from '@hua-labs/motion-core';
17
+ import { useUnifiedMotion } from '@hua-labs/motion-core';
18
+ import type { UseUnifiedMotionOptions } from '@hua-labs/motion-core';
19
+
20
+ /**
21
+ * Motion type (motion-core의 EntranceType과 동일)
22
+ */
23
+ export type MotionType = EntranceType;
24
+
25
+ /**
26
+ * useMotion options (motion-core의 UseUnifiedMotionOptions와 동일)
27
+ */
28
+ export type UseMotionOptions = UseUnifiedMotionOptions;
29
+
30
+ /**
31
+ * 통합 Motion Hook
32
+ *
33
+ * motion-core의 useUnifiedMotion을 래핑하여 hua-ux 프레임워크에서 사용합니다.
34
+ *
35
+ * Wraps motion-core's useUnifiedMotion for use in the hua-ux framework.
36
+ *
37
+ * @param options - Motion options
38
+ * @returns Motion result with ref and control functions
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * const motion = useMotion({
43
+ * type: 'fadeIn',
44
+ * duration: 600,
45
+ * autoStart: false,
46
+ * });
47
+ *
48
+ * return <div ref={motion.ref} style={motion.style}>Content</div>;
49
+ * ```
50
+ */
51
+ export function useMotion<T extends MotionElement = HTMLDivElement>(
52
+ options: UseMotionOptions
53
+ ): BaseMotionReturn<T> {
54
+ // motion-core의 useUnifiedMotion을 직접 사용
55
+ // Directly use motion-core's useUnifiedMotion
56
+ return useUnifiedMotion<T>(options);
57
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework
3
+ *
4
+ * Framework layer for hua-ux
5
+ */
6
+
7
+ // Components
8
+ export { HuaUxLayout } from './components/HuaUxLayout';
9
+ export { HuaUxPage } from './components/HuaUxPage';
10
+ export { UnifiedProviders } from './components/Providers';
11
+ export { BrandedButton } from './components/BrandedButton';
12
+ export { BrandedCard } from './components/BrandedCard';
13
+ export { ErrorBoundary } from './components/ErrorBoundary';
14
+ export type { ErrorBoundaryProps } from './components/ErrorBoundary';
15
+
16
+ // Configuration
17
+ export { defineConfig, loadConfig, getConfig, setConfig, resetConfig } from './config';
18
+ export type { HuaUxConfig, PresetName } from './types';
19
+
20
+ // Data Fetching
21
+ export { useData, fetchData } from './utils/data-fetching';
22
+ export type { DataFetchResult } from './utils/data-fetching';
23
+
24
+ // Middleware
25
+ export { createI18nMiddleware } from './middleware/i18n';
26
+ export type { I18nMiddlewareConfig } from './middleware/i18n';
27
+
28
+ // File Structure
29
+ // 서버 전용 유틸리티 (클라이언트 번들에서 제외)
30
+ // Server-only utilities (excluded from client bundle)
31
+ // export { validateFileStructure } from './utils/file-structure';
32
+ // export type { FileStructureResult } from './utils/file-structure';
33
+ // Note: validateFileStructure는 서버 전용이므로 필요시 직접 import
34
+
35
+ // Metadata Utilities
36
+ export { generatePageMetadata } from './utils/metadata';
37
+ export type { SEOConfig } from './utils/metadata';
38
+
39
+ // GEO (Generative Engine Optimization)
40
+ export {
41
+ generateGEOMetadata,
42
+ renderJSONLD,
43
+ createAIContext,
44
+ generateSoftwareApplicationLD,
45
+ generateFAQPageLD,
46
+ generateTechArticleLD,
47
+ generateHowToLD,
48
+ } from './seo/geo';
49
+ export type {
50
+ GEOConfig,
51
+ GEOMetadata,
52
+ StructuredData,
53
+ SoftwareApplicationType,
54
+ SoftwareCategory,
55
+ ProgrammingLanguage,
56
+ } from './seo/geo';
57
+
58
+ // License System
59
+ export {
60
+ initLicense,
61
+ getLicense,
62
+ checkLicense,
63
+ hasLicense,
64
+ requireLicense
65
+ } from './license';
66
+ export type {
67
+ LicenseInfo,
68
+ LicenseType,
69
+ LicenseFeature,
70
+ LicenseCheckResult
71
+ } from './license/types';
72
+
73
+ // Plugin System
74
+ export {
75
+ pluginRegistry,
76
+ registerPlugin,
77
+ getPlugin,
78
+ getAllPlugins
79
+ } from './plugins';
80
+ export type { HuaUxPlugin } from './plugins/types';
81
+
82
+ // Branding
83
+ export { BrandingProvider, useBranding, useBrandingColor } from './branding/context';
84
+ export { generateCSSVariables, generateCSSVariablesObject } from './branding/css-vars';
85
+ export { generateTailwindConfig } from './branding/tailwind-config';
86
+
87
+ // Accessibility (a11y)
88
+ export {
89
+ useFocusManagement,
90
+ useFocusTrap,
91
+ SkipToContent,
92
+ LiveRegion,
93
+ useLiveRegion,
94
+ } from './a11y';
95
+ export type {
96
+ FocusManagementOptions,
97
+ FocusTrapOptions,
98
+ SkipToContentProps,
99
+ LiveRegionProps,
100
+ } from './a11y';
101
+
102
+ // Loading
103
+ export {
104
+ useDelayedLoading,
105
+ useLoadingState,
106
+ Skeleton,
107
+ SkeletonGroup,
108
+ SuspenseWrapper,
109
+ withSuspense,
110
+ } from './loading';
111
+ export type {
112
+ DelayedLoadingOptions,
113
+ SkeletonGroupProps,
114
+ SuspenseWrapperProps,
115
+ } from './loading';
116
+
117
+ // Motion Hooks
118
+ export { useMotion } from './hooks/useMotion';
119
+ export type { MotionType, UseMotionOptions } from './hooks/useMotion';
120
+
121
+ // Types
122
+ export type { HuaUxLayoutProps, HuaUxPageProps } from './types';
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @hua-labs/hua-ux/framework - License Errors
3
+ *
4
+ * 라이선스 관련 에러 메시지
5
+ */
6
+
7
+ import type { LicenseFeature } from './types';
8
+
9
+ /**
10
+ * 구매 링크 (향후 실제 링크로 변경)
11
+ */
12
+ const PURCHASE_URL = 'https://hua-labs.com/pricing';
13
+
14
+ /**
15
+ * 라이선스 에러 메시지 생성
16
+ *
17
+ * @param feature - 필요한 기능
18
+ * @param currentLicense - 현재 라이선스 타입
19
+ * @returns 에러 메시지
20
+ */
21
+ export function createLicenseError(
22
+ feature: LicenseFeature,
23
+ currentLicense: 'free' | 'pro' | 'enterprise' = 'free'
24
+ ): string {
25
+ const featureNames: Record<LicenseFeature, string> = {
26
+ 'core': 'Core features',
27
+ 'motion-basic': 'Basic motion',
28
+ 'motion-pro': 'Motion Pro',
29
+ 'i18n-basic': 'Basic i18n',
30
+ 'i18n-pro': 'i18n Pro',
31
+ 'preset-basic': 'Basic presets',
32
+ 'preset-pro': 'Preset Pro',
33
+ 'white-labeling': 'White Labeling',
34
+ };
35
+
36
+ const featureName = featureNames[feature] || feature;
37
+
38
+ // 필요한 라이선스 타입 결정
39
+ let requiredLicense: 'pro' | 'enterprise' = 'pro';
40
+ if (feature === 'white-labeling') {
41
+ requiredLicense = 'enterprise';
42
+ }
43
+
44
+ const messages = {
45
+ ko: [
46
+ `[hua-ux] ❌ "${featureName}" 기능을 사용하려면 ${requiredLicense === 'enterprise' ? 'Enterprise' : 'Pro'} 라이선스가 필요합니다.`,
47
+ `[hua-ux] ❌ Feature "${featureName}" requires a ${requiredLicense === 'enterprise' ? 'Enterprise' : 'Pro'} license.`,
48
+ '',
49
+ `현재 라이선스: ${currentLicense === 'free' ? 'Free' : currentLicense === 'pro' ? 'Pro' : 'Enterprise'}`,
50
+ `Current license: ${currentLicense === 'free' ? 'Free' : currentLicense === 'pro' ? 'Pro' : 'Enterprise'}`,
51
+ '',
52
+ `💡 해결 방법 / Solution:`,
53
+ ` - ${requiredLicense === 'enterprise' ? 'Enterprise' : 'Pro'} 라이선스를 구매하세요.`,
54
+ ` - Purchase a ${requiredLicense === 'enterprise' ? 'Enterprise' : 'Pro'} license.`,
55
+ ` - 구매 링크: ${PURCHASE_URL}`,
56
+ ` - Purchase link: ${PURCHASE_URL}`,
57
+ '',
58
+ `📖 가이드 / Guide: https://github.com/HUA-Labs/hua-platform/tree/main/packages/hua-ux/docs`,
59
+ ],
60
+ };
61
+
62
+ return messages.ko.join('\n');
63
+ }