@nexus-cross/design-system 2.0.0-beta.1 → 2.0.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.
@@ -99,6 +99,74 @@ const nextConfig = {
99
99
 
100
100
  ---
101
101
 
102
+ ## Global Setup — `<Toaster />` / `<ModalContainer />`
103
+
104
+ `Toaster` / `ModalContainer`는 내부적으로 `useState`를 사용하는 **클라이언트 컴포넌트**입니다.
105
+
106
+ | 환경 | 패턴 |
107
+ |---|---|
108
+ | Next.js App Router (root layout이 server component) | **반드시 `"use client"` wrapper로 분리** |
109
+ | Vite, CRA, Pages Router 등 root가 이미 client | 직접 import 가능 |
110
+
111
+ ### Pattern A — Next.js App Router (권장)
112
+
113
+ ```tsx
114
+ // app/providers.tsx
115
+ "use client";
116
+
117
+ import { Toaster } from '@nexus-cross/design-system';
118
+ import { ModalContainer } from '@nexus-cross/design-system/modal';
119
+
120
+ export function NexusProviders() {
121
+ return (
122
+ <>
123
+ <ModalContainer />
124
+ <Toaster position="top-right" />
125
+ </>
126
+ );
127
+ }
128
+ ```
129
+
130
+ ```tsx
131
+ // app/layout.tsx (server component — "use client" 추가 금지)
132
+ import { NexusProviders } from './providers';
133
+ import './globals.css';
134
+
135
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
136
+ return (
137
+ <html lang="ko">
138
+ <body>
139
+ {children}
140
+ <NexusProviders />
141
+ </body>
142
+ </html>
143
+ );
144
+ }
145
+ ```
146
+
147
+ > ❌ server `layout.tsx`에서 `Toaster` / `ModalContainer`를 직접 import하면
148
+ > `TypeError: useState only works in Client Components.` 에러 발생.
149
+
150
+ ### Pattern B — Vite / CRA / 이미 client인 root
151
+
152
+ ```tsx
153
+ // App.tsx
154
+ import { Toaster } from '@nexus-cross/design-system';
155
+ import { ModalContainer } from '@nexus-cross/design-system/modal';
156
+
157
+ export default function App() {
158
+ return (
159
+ <>
160
+ {/* routes / pages */}
161
+ <ModalContainer />
162
+ <Toaster position="top-right" />
163
+ </>
164
+ );
165
+ }
166
+ ```
167
+
168
+ ---
169
+
102
170
  ## Token Path Convention
103
171
 
104
172
  모든 디자인 토큰은 5-segment 형식: `color.semantic.{namespace}.{slot}.{state}`
@@ -203,15 +203,62 @@ The following components MUST be placed at the app root (layout.tsx or App.tsx).
203
203
  | `<ModalContainer />` | `@nexus-cross/design-system/modal` | Using `modal()` or `useModal()` | Modal rendering container |
204
204
  | `<Toaster />` | `@nexus-cross/design-system` | Using `toast()` | Toast notification renderer |
205
205
 
206
+ > Both components use `useState` internally → they are **client components**.
207
+ > In environments where the root layout is a **server component** (e.g. Next.js App Router), import them through a `"use client"` wrapper as shown in **Pattern A**.
208
+ > If the root layout is already a client component (e.g. Vite, CRA, Pages Router with `"use client"` already declared), you can use them directly as in **Pattern B**.
209
+
210
+ ### Pattern A — Next.js App Router / RSC (Recommended)
211
+
212
+ Server components cannot directly render client components that use hooks. Wrap providers in a single client file:
213
+
206
214
  ```tsx
207
- // layout.tsx or App.tsx
215
+ // app/providers.tsx
216
+ "use client";
217
+
218
+ import { Toaster } from '@nexus-cross/design-system';
208
219
  import { ModalContainer } from '@nexus-cross/design-system/modal';
220
+
221
+ export function NexusProviders() {
222
+ return (
223
+ <>
224
+ <ModalContainer />
225
+ <Toaster position="top-right" />
226
+ </>
227
+ );
228
+ }
229
+ ```
230
+
231
+ ```tsx
232
+ // app/layout.tsx (server component — DO NOT add "use client" here)
233
+ import { NexusProviders } from './providers';
234
+ import './globals.css';
235
+
236
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
237
+ return (
238
+ <html lang="en">
239
+ <body>
240
+ {children}
241
+ <NexusProviders />
242
+ </body>
243
+ </html>
244
+ );
245
+ }
246
+ ```
247
+
248
+ > ❌ Importing `Toaster` / `ModalContainer` directly inside a server `layout.tsx` throws:
249
+ > `TypeError: useState only works in Client Components.`
250
+
251
+ ### Pattern B — Vite / CRA / Already-client roots
252
+
253
+ ```tsx
254
+ // App.tsx (already a client component)
209
255
  import { Toaster } from '@nexus-cross/design-system';
256
+ import { ModalContainer } from '@nexus-cross/design-system/modal';
210
257
 
211
- export default function RootLayout({ children }) {
258
+ export default function App() {
212
259
  return (
213
260
  <>
214
- {children}
261
+ {/* ... your routes/pages ... */}
215
262
  <ModalContainer />
216
263
  <Toaster position="top-right" />
217
264
  </>
@@ -1023,7 +1023,10 @@ function MyModal({ close, resolve }: { close: () => void; resolve: (value: any)
1023
1023
  ```tsx
1024
1024
  import { modal, useModal, ModalContainer } from '@nexus-cross/design-system/modal';
1025
1025
 
1026
- // ModalContainer MUST be placed at the app root
1026
+ // ModalContainer MUST be placed at the app root.
1027
+ // It uses useState internally → it's a client component.
1028
+ // In Next.js App Router, wrap it in a "use client" file (e.g. providers.tsx)
1029
+ // and render that wrapper from the (server) layout. NEVER add "use client" to layout.tsx itself.
1027
1030
  <ModalContainer />
1028
1031
 
1029
1032
  // Method 1: modal() function
@@ -2164,7 +2167,10 @@ Toast notifications. Sonner-based.
2164
2167
  ```tsx
2165
2168
  import { toast, Toaster } from '@nexus-cross/design-system';
2166
2169
 
2167
- // Place Toaster at app root
2170
+ // Place Toaster at the app root.
2171
+ // It uses useState internally → it's a client component.
2172
+ // In Next.js App Router, wrap it in a "use client" file (e.g. providers.tsx)
2173
+ // and render that wrapper from the (server) layout. NEVER add "use client" to layout.tsx itself.
2168
2174
  <Toaster position="top-right" />
2169
2175
 
2170
2176
  // Usage
@@ -48,9 +48,34 @@ var NxImage = React__namespace.forwardRef(
48
48
  const [status, setStatus] = React__namespace.useState(
49
49
  "loading"
50
50
  );
51
+ const [currentSrc, setCurrentSrc] = React__namespace.useState(src);
52
+ const innerRef = React__namespace.useRef(null);
53
+ const setRefs = React__namespace.useCallback(
54
+ (node) => {
55
+ innerRef.current = node;
56
+ if (typeof ref === "function") {
57
+ ref(node);
58
+ } else if (ref) {
59
+ ref.current = node;
60
+ }
61
+ },
62
+ [ref]
63
+ );
51
64
  React__namespace.useEffect(() => {
52
- setStatus("loading");
65
+ setCurrentSrc(src);
53
66
  }, [src]);
67
+ React__namespace.useEffect(() => {
68
+ setStatus("loading");
69
+ const img = innerRef.current;
70
+ if (!img || !currentSrc) return;
71
+ if (img.complete) {
72
+ if (img.naturalHeight > 0) {
73
+ setStatus("loaded");
74
+ } else if (img.src) {
75
+ setStatus("error");
76
+ }
77
+ }
78
+ }, [currentSrc]);
54
79
  const handleLoad = React__namespace.useCallback(
55
80
  (e) => {
56
81
  setStatus("loaded");
@@ -60,14 +85,14 @@ var NxImage = React__namespace.forwardRef(
60
85
  );
61
86
  const handleError = React__namespace.useCallback(
62
87
  (e) => {
63
- if (fallbackSrc && e.currentTarget.src !== fallbackSrc) {
64
- e.currentTarget.src = fallbackSrc;
88
+ if (fallbackSrc && currentSrc !== fallbackSrc) {
89
+ setCurrentSrc(fallbackSrc);
65
90
  return;
66
91
  }
67
92
  setStatus("error");
68
93
  onError?.(e);
69
94
  },
70
- [fallbackSrc, onError]
95
+ [fallbackSrc, currentSrc, onError]
71
96
  );
72
97
  const wrapperStyle = {
73
98
  ...style,
@@ -84,8 +109,8 @@ var NxImage = React__namespace.forwardRef(
84
109
  /* @__PURE__ */ jsxRuntime.jsx(
85
110
  "img",
86
111
  {
87
- ref,
88
- src,
112
+ ref: setRefs,
113
+ src: currentSrc,
89
114
  alt: alt ?? "",
90
115
  loading: lazy ? "lazy" : "eager",
91
116
  className: chunkCZC76ZD5_js.cn(
@@ -26,9 +26,34 @@ var NxImage = React.forwardRef(
26
26
  const [status, setStatus] = React.useState(
27
27
  "loading"
28
28
  );
29
+ const [currentSrc, setCurrentSrc] = React.useState(src);
30
+ const innerRef = React.useRef(null);
31
+ const setRefs = React.useCallback(
32
+ (node) => {
33
+ innerRef.current = node;
34
+ if (typeof ref === "function") {
35
+ ref(node);
36
+ } else if (ref) {
37
+ ref.current = node;
38
+ }
39
+ },
40
+ [ref]
41
+ );
29
42
  React.useEffect(() => {
30
- setStatus("loading");
43
+ setCurrentSrc(src);
31
44
  }, [src]);
45
+ React.useEffect(() => {
46
+ setStatus("loading");
47
+ const img = innerRef.current;
48
+ if (!img || !currentSrc) return;
49
+ if (img.complete) {
50
+ if (img.naturalHeight > 0) {
51
+ setStatus("loaded");
52
+ } else if (img.src) {
53
+ setStatus("error");
54
+ }
55
+ }
56
+ }, [currentSrc]);
32
57
  const handleLoad = React.useCallback(
33
58
  (e) => {
34
59
  setStatus("loaded");
@@ -38,14 +63,14 @@ var NxImage = React.forwardRef(
38
63
  );
39
64
  const handleError = React.useCallback(
40
65
  (e) => {
41
- if (fallbackSrc && e.currentTarget.src !== fallbackSrc) {
42
- e.currentTarget.src = fallbackSrc;
66
+ if (fallbackSrc && currentSrc !== fallbackSrc) {
67
+ setCurrentSrc(fallbackSrc);
43
68
  return;
44
69
  }
45
70
  setStatus("error");
46
71
  onError?.(e);
47
72
  },
48
- [fallbackSrc, onError]
73
+ [fallbackSrc, currentSrc, onError]
49
74
  );
50
75
  const wrapperStyle = {
51
76
  ...style,
@@ -62,8 +87,8 @@ var NxImage = React.forwardRef(
62
87
  /* @__PURE__ */ jsx(
63
88
  "img",
64
89
  {
65
- ref,
66
- src,
90
+ ref: setRefs,
91
+ src: currentSrc,
67
92
  alt: alt ?? "",
68
93
  loading: lazy ? "lazy" : "eager",
69
94
  className: cn(
@@ -1 +1 @@
1
- {"version":3,"file":"NxImage.d.ts","sourceRoot":"","sources":["../../src/components/NxImage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAW/B,UAAU,YAAa,SAAQ,KAAK,CAAC,iBAAiB,CAAC,gBAAgB,CAAC;IACtE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAClD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,QAAA,MAAM,OAAO,uFAwFZ,CAAC;AAIF,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"NxImage.d.ts","sourceRoot":"","sources":["../../src/components/NxImage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAW/B,UAAU,YAAa,SAAQ,KAAK,CAAC,iBAAiB,CAAC,gBAAgB,CAAC;IACtE,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IAClD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,QAAA,MAAM,OAAO,uFA8IZ,CAAC;AAIF,OAAO,EAAE,OAAO,EAAE,CAAC;AACnB,YAAY,EAAE,YAAY,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@ var chunkC2DSAJTL_js = require('./chunks/chunk-C2DSAJTL.js');
7
7
  var chunkK3CK7NTP_js = require('./chunks/chunk-K3CK7NTP.js');
8
8
  var chunkDICN6GKE_js = require('./chunks/chunk-DICN6GKE.js');
9
9
  var chunkDYPPVXQF_js = require('./chunks/chunk-DYPPVXQF.js');
10
- var chunkNZHK76R3_js = require('./chunks/chunk-NZHK76R3.js');
10
+ var chunkLYPBQI3Y_js = require('./chunks/chunk-LYPBQI3Y.js');
11
11
  var chunkLTS674LF_js = require('./chunks/chunk-LTS674LF.js');
12
12
  var chunkLAGQ7J5A_js = require('./chunks/chunk-LAGQ7J5A.js');
13
13
  var chunkQK6NCII4_js = require('./chunks/chunk-QK6NCII4.js');
@@ -430,7 +430,7 @@ Object.defineProperty(exports, "tagInputVariants", {
430
430
  });
431
431
  Object.defineProperty(exports, "NxImage", {
432
432
  enumerable: true,
433
- get: function () { return chunkNZHK76R3_js.NxImage; }
433
+ get: function () { return chunkLYPBQI3Y_js.NxImage; }
434
434
  });
435
435
  Object.defineProperty(exports, "DatePicker", {
436
436
  enumerable: true,
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuRoot,
5
5
  export { ToggleGroup, toggleGroupVariants } from './chunks/chunk-PIGHBDK5.mjs';
6
6
  export { Slider, sliderVariants } from './chunks/chunk-H2G5FMRN.mjs';
7
7
  export { TagInput, tagInputVariants } from './chunks/chunk-H2V7RHYV.mjs';
8
- export { NxImage } from './chunks/chunk-6BWOKTVQ.mjs';
8
+ export { NxImage } from './chunks/chunk-WZFKTTVX.mjs';
9
9
  export { DatePicker } from './chunks/chunk-FBC53TOS.mjs';
10
10
  export { ImageUpload, imageUploadVariants } from './chunks/chunk-5J63FUAS.mjs';
11
11
  export { ClientOnly } from './chunks/chunk-AOXXE5UQ.mjs';
package/dist/nx-image.js CHANGED
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkNZHK76R3_js = require('./chunks/chunk-NZHK76R3.js');
3
+ var chunkLYPBQI3Y_js = require('./chunks/chunk-LYPBQI3Y.js');
4
4
  require('./chunks/chunk-CZC76ZD5.js');
5
5
 
6
6
 
7
7
 
8
8
  Object.defineProperty(exports, "NxImage", {
9
9
  enumerable: true,
10
- get: function () { return chunkNZHK76R3_js.NxImage; }
10
+ get: function () { return chunkLYPBQI3Y_js.NxImage; }
11
11
  });
package/dist/nx-image.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export { NxImage } from './chunks/chunk-6BWOKTVQ.mjs';
1
+ export { NxImage } from './chunks/chunk-WZFKTTVX.mjs';
2
2
  import './chunks/chunk-MCKOWMLS.mjs';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Company CSS — Tailwind v4 (@theme + .dark)
3
3
  * 자동 생성: scripts/generate-css.js
4
- * 생성일: 2026-04-29T08:27:18.189Z
4
+ * 생성일: 2026-05-06T03:32:31.081Z
5
5
  *
6
6
  * @theme: Tailwind 유틸리티 클래스 자동 생성
7
7
  * :root: 순수 CSS 변수 (var()로 직접 사용)
@@ -2,7 +2,7 @@
2
2
  * Company CSS — 순수 CSS 변수 (:root + .dark)
3
3
  * Tailwind 없이 var(--*) 로 사용 가능
4
4
  * 자동 생성: scripts/generate-css.js
5
- * 생성일: 2026-04-29T08:27:18.193Z
5
+ * 생성일: 2026-05-06T03:32:31.085Z
6
6
  */
7
7
 
8
8
  :root {
@@ -2,7 +2,7 @@
2
2
  * Prediction Domain CSS — 순수 CSS 변수
3
3
  * Tailwind 없이 var(--*) 로 사용 가능
4
4
  * 자동 생성: scripts/generate-css.js
5
- * 생성일: 2026-04-29T08:27:18.199Z
5
+ * 생성일: 2026-05-06T03:32:31.090Z
6
6
  */
7
7
 
8
8
  :root {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Prediction Domain CSS — Tailwind v4 (@theme + .prediction)
3
3
  * 자동 생성: scripts/generate-css.js
4
- * 생성일: 2026-04-29T08:27:18.198Z
4
+ * 생성일: 2026-05-06T03:32:31.089Z
5
5
  */
6
6
 
7
7
  @theme {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexus-cross/design-system",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0",
4
4
  "description": "NEXUS Design System UI Components",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -362,8 +362,8 @@
362
362
  "sonner": "^2.0.7",
363
363
  "vaul": "^1.1.2",
364
364
  "zod": "^3.25.76",
365
- "@nexus-cross/tokens": "2.0.0-beta.1",
366
- "@nexus-cross/tokens-domains": "2.0.0-beta.1"
365
+ "@nexus-cross/tokens": "2.0.0",
366
+ "@nexus-cross/tokens-domains": "2.0.0"
367
367
  },
368
368
  "devDependencies": {
369
369
  "@testing-library/jest-dom": "^6.9.1",