@demokit-ai/react 0.3.0 → 0.4.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/dist/banner.d.ts +26 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/context.d.ts +7 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/guard.d.ts +59 -0
- package/dist/guard.d.ts.map +1 -0
- package/dist/hooks.d.ts +59 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/index.cjs +67 -27
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +23 -460
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +65 -27
- package/dist/index.js.map +1 -1
- package/dist/powered-by.d.ts +46 -0
- package/dist/powered-by.d.ts.map +1 -0
- package/dist/provider.d.ts +50 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/toggle.d.ts +76 -0
- package/dist/toggle.d.ts.map +1 -0
- package/dist/types.d.ts +229 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +20 -30
- package/LICENSE +0 -190
- package/dist/index.d.cts +0 -480
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DemoModeBannerProps } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* A ready-to-use banner component that shows when demo mode is active
|
|
4
|
+
*
|
|
5
|
+
* Displays a prominent amber banner with a label, description, and exit button.
|
|
6
|
+
* Automatically hides when demo mode is disabled or before hydration.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* function App() {
|
|
10
|
+
* return (
|
|
11
|
+
* <DemoKitProvider fixtures={fixtures}>
|
|
12
|
+
* <DemoModeBanner />
|
|
13
|
+
* <YourApp />
|
|
14
|
+
* </DemoKitProvider>
|
|
15
|
+
* )
|
|
16
|
+
* }
|
|
17
|
+
*
|
|
18
|
+
* @example Custom labels
|
|
19
|
+
* <DemoModeBanner
|
|
20
|
+
* demoLabel="Preview Mode"
|
|
21
|
+
* description="You're viewing sample data"
|
|
22
|
+
* exitLabel="Exit Preview"
|
|
23
|
+
* />
|
|
24
|
+
*/
|
|
25
|
+
export declare function DemoModeBanner({ className, exitLabel, demoLabel, description, showIcon, showPoweredBy, poweredByUrl, style, onExit, }: DemoModeBannerProps): import("react/jsx-runtime").JSX.Element | null;
|
|
26
|
+
//# sourceMappingURL=banner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.d.ts","sourceRoot":"","sources":["../src/banner.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAqElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,cAAc,CAAC,EAC7B,SAAc,EACd,SAA4B,EAC5B,SAA8B,EAC9B,WAAmD,EACnD,QAAe,EACf,aAAoB,EACpB,YAAmC,EACnC,KAAK,EACL,MAAM,GACP,EAAE,mBAAmB,kDA0DrB"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { RemoteConfig } from '@demokit-ai/core';
|
|
2
|
+
/**
|
|
3
|
+
* Create a remote source configuration for fetching fixtures from DemoKit Cloud
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```tsx
|
|
7
|
+
* import { createRemoteSource } from '@demokit-ai/react'
|
|
8
|
+
*
|
|
9
|
+
* const source = createRemoteSource({
|
|
10
|
+
* apiUrl: process.env.NEXT_PUBLIC_DEMOKIT_API_URL!,
|
|
11
|
+
* apiKey: process.env.NEXT_PUBLIC_DEMOKIT_API_KEY!,
|
|
12
|
+
* })
|
|
13
|
+
*
|
|
14
|
+
* <DemoKitProvider source={source}>
|
|
15
|
+
* {children}
|
|
16
|
+
* </DemoKitProvider>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function createRemoteSource(config: RemoteConfig): RemoteConfig;
|
|
20
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAOrE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnD;;;GAGG;AACH,eAAO,MAAM,eAAe,2DAA6D,CAAA"}
|
package/dist/guard.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface UseDemoGuardOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Called when a mutation is blocked in demo mode.
|
|
4
|
+
* Use this to show a toast or notification to the user.
|
|
5
|
+
* @param message - The action name or a default message
|
|
6
|
+
*/
|
|
7
|
+
onBlocked?: (message: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export interface DemoGuardReturn {
|
|
10
|
+
/**
|
|
11
|
+
* Whether demo mode is currently active
|
|
12
|
+
*/
|
|
13
|
+
isDemoMode: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Wraps a mutation action — prevents execution in demo mode.
|
|
16
|
+
* When blocked, calls `onBlocked` with the action name.
|
|
17
|
+
*
|
|
18
|
+
* @param action - The mutation function to execute (only runs if NOT in demo mode)
|
|
19
|
+
* @param actionName - Human-readable name (e.g., "Changes saved")
|
|
20
|
+
* @returns `true` if the action was executed, `false` if blocked
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* const { guardMutation } = useDemoGuard({
|
|
25
|
+
* onBlocked: (msg) => toast.success(msg)
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* guardMutation(() => sdk.deleteItem(id), 'Item deleted')
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
guardMutation: (action: () => void | Promise<void>, actionName?: string) => boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Hook that prevents mutations from executing in demo mode.
|
|
35
|
+
*
|
|
36
|
+
* Instead of letting mutations hit the (intercepted) API and trigger
|
|
37
|
+
* side effects like optimistic updates and onSuccess handlers, this
|
|
38
|
+
* hook blocks the mutation entirely and optionally notifies the user.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* import { useDemoGuard } from '@demokit-ai/react'
|
|
43
|
+
* import { toast } from 'sonner'
|
|
44
|
+
*
|
|
45
|
+
* function MyComponent() {
|
|
46
|
+
* const { guardMutation } = useDemoGuard({
|
|
47
|
+
* onBlocked: (msg) => toast.success(msg, {
|
|
48
|
+
* description: 'Changes are not saved. Exit demo mode to make real changes.',
|
|
49
|
+
* })
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* const handleDelete = () => {
|
|
53
|
+
* guardMutation(() => sdk.deleteItem(id), 'Item deleted')
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function useDemoGuard(options?: UseDemoGuardOptions): DemoGuardReturn;
|
|
59
|
+
//# sourceMappingURL=guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../src/guard.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACtC;AAED,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,UAAU,EAAE,OAAO,CAAA;IAEnB;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,EAAE,CACb,MAAM,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,KAChB,OAAO,CAAA;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,eAAe,CAyB/E"}
|
package/dist/hooks.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { DemoModeContextValue } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Hook to access demo mode state and controls
|
|
4
|
+
*
|
|
5
|
+
* @returns Demo mode context value with state and control methods
|
|
6
|
+
* @throws Error if used outside of DemoKitProvider
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* function MyComponent() {
|
|
10
|
+
* const { isDemoMode, isHydrated, toggle } = useDemoMode()
|
|
11
|
+
*
|
|
12
|
+
* // Wait for hydration before rendering demo-dependent UI
|
|
13
|
+
* if (!isHydrated) {
|
|
14
|
+
* return <Loading />
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* return (
|
|
18
|
+
* <div>
|
|
19
|
+
* <p>Demo mode: {isDemoMode ? 'ON' : 'OFF'}</p>
|
|
20
|
+
* <button onClick={toggle}>Toggle</button>
|
|
21
|
+
* </div>
|
|
22
|
+
* )
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
export declare function useDemoMode(): DemoModeContextValue;
|
|
26
|
+
/**
|
|
27
|
+
* Hook to check if demo mode is enabled
|
|
28
|
+
* Shorthand for useDemoMode().isDemoMode
|
|
29
|
+
*
|
|
30
|
+
* @returns Whether demo mode is enabled
|
|
31
|
+
*/
|
|
32
|
+
export declare function useIsDemoMode(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Hook to check if the component has hydrated
|
|
35
|
+
* Shorthand for useDemoMode().isHydrated
|
|
36
|
+
*
|
|
37
|
+
* @returns Whether the component has hydrated
|
|
38
|
+
*/
|
|
39
|
+
export declare function useIsHydrated(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Hook to access the session state
|
|
42
|
+
* Shorthand for useDemoMode().getSession()
|
|
43
|
+
*
|
|
44
|
+
* @returns The session state, or null if not yet initialized
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* function MyComponent() {
|
|
48
|
+
* const session = useDemoSession()
|
|
49
|
+
*
|
|
50
|
+
* const cart = session?.get<CartItem[]>('cart') || []
|
|
51
|
+
* const addToCart = (item: CartItem) => {
|
|
52
|
+
* session?.set('cart', [...cart, item])
|
|
53
|
+
* }
|
|
54
|
+
*
|
|
55
|
+
* return <CartView items={cart} onAdd={addToCart} />
|
|
56
|
+
* }
|
|
57
|
+
*/
|
|
58
|
+
export declare function useDemoSession(): import("@demokit-ai/core").SessionState | null;
|
|
59
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,WAAW,IAAI,oBAAoB,CAWlD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,mDAE7B"}
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,8 @@ __export(index_exports, {
|
|
|
25
25
|
DemoModeContext: () => DemoModeContext,
|
|
26
26
|
DemoModeToggle: () => DemoModeToggle,
|
|
27
27
|
PoweredByBadge: () => PoweredByBadge,
|
|
28
|
+
createRemoteSource: () => createRemoteSource,
|
|
29
|
+
useDemoGuard: () => useDemoGuard,
|
|
28
30
|
useDemoMode: () => useDemoMode,
|
|
29
31
|
useDemoSession: () => useDemoSession,
|
|
30
32
|
useIsDemoMode: () => useIsDemoMode,
|
|
@@ -47,11 +49,7 @@ function DemoKitProvider({
|
|
|
47
49
|
children,
|
|
48
50
|
fixtures,
|
|
49
51
|
// Remote config
|
|
50
|
-
|
|
51
|
-
cloudUrl,
|
|
52
|
-
timeout,
|
|
53
|
-
retry,
|
|
54
|
-
maxRetries,
|
|
52
|
+
source,
|
|
55
53
|
onRemoteLoad,
|
|
56
54
|
onRemoteError,
|
|
57
55
|
loadingFallback = null,
|
|
@@ -60,11 +58,16 @@ function DemoKitProvider({
|
|
|
60
58
|
storageKey = "demokit-mode",
|
|
61
59
|
initialEnabled = false,
|
|
62
60
|
onDemoModeChange,
|
|
63
|
-
baseUrl
|
|
61
|
+
baseUrl,
|
|
62
|
+
// Detection & guards
|
|
63
|
+
detection,
|
|
64
|
+
canDisable,
|
|
65
|
+
onMutationIntercepted
|
|
64
66
|
}) {
|
|
65
67
|
const [isDemoMode, setIsDemoMode] = (0, import_react2.useState)(initialEnabled);
|
|
66
68
|
const [isHydrated, setIsHydrated] = (0, import_react2.useState)(false);
|
|
67
|
-
const [
|
|
69
|
+
const [isPublicDemo, setIsPublicDemo] = (0, import_react2.useState)(false);
|
|
70
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(!!source?.apiKey);
|
|
68
71
|
const [remoteError, setRemoteError] = (0, import_react2.useState)(null);
|
|
69
72
|
const [remoteVersion, setRemoteVersion] = (0, import_react2.useState)(null);
|
|
70
73
|
const interceptorRef = (0, import_react2.useRef)(null);
|
|
@@ -79,6 +82,9 @@ function DemoKitProvider({
|
|
|
79
82
|
storageKey,
|
|
80
83
|
initialEnabled,
|
|
81
84
|
baseUrl,
|
|
85
|
+
detection,
|
|
86
|
+
canDisable,
|
|
87
|
+
onMutationIntercepted,
|
|
82
88
|
onEnable: () => {
|
|
83
89
|
setIsDemoMode(true);
|
|
84
90
|
onDemoModeChange?.(true);
|
|
@@ -90,21 +96,22 @@ function DemoKitProvider({
|
|
|
90
96
|
});
|
|
91
97
|
const storedState = interceptorRef.current.isEnabled();
|
|
92
98
|
setIsDemoMode(storedState);
|
|
99
|
+
setIsPublicDemo(interceptorRef.current.isPublicDemo());
|
|
93
100
|
setIsHydrated(true);
|
|
94
101
|
},
|
|
95
|
-
[storageKey, initialEnabled, baseUrl, onDemoModeChange]
|
|
102
|
+
[storageKey, initialEnabled, baseUrl, onDemoModeChange, detection, canDisable, onMutationIntercepted]
|
|
96
103
|
);
|
|
97
104
|
const fetchAndSetup = (0, import_react2.useCallback)(async () => {
|
|
98
|
-
if (!apiKey) return;
|
|
105
|
+
if (!source?.apiKey) return;
|
|
99
106
|
setIsLoading(true);
|
|
100
107
|
setRemoteError(null);
|
|
101
108
|
try {
|
|
102
109
|
const response = await (0, import_core.fetchCloudFixtures)({
|
|
103
|
-
apiKey,
|
|
104
|
-
|
|
105
|
-
timeout,
|
|
106
|
-
retry,
|
|
107
|
-
maxRetries,
|
|
110
|
+
apiKey: source.apiKey,
|
|
111
|
+
apiUrl: source.apiUrl,
|
|
112
|
+
timeout: source.timeout,
|
|
113
|
+
retry: source.retry,
|
|
114
|
+
maxRetries: source.maxRetries,
|
|
108
115
|
onLoad: onRemoteLoad,
|
|
109
116
|
onError: onRemoteError
|
|
110
117
|
});
|
|
@@ -125,11 +132,7 @@ function DemoKitProvider({
|
|
|
125
132
|
setIsLoading(false);
|
|
126
133
|
}
|
|
127
134
|
}, [
|
|
128
|
-
|
|
129
|
-
cloudUrl,
|
|
130
|
-
timeout,
|
|
131
|
-
retry,
|
|
132
|
-
maxRetries,
|
|
135
|
+
source,
|
|
133
136
|
fixtures,
|
|
134
137
|
onRemoteLoad,
|
|
135
138
|
onRemoteError,
|
|
@@ -141,7 +144,7 @@ function DemoKitProvider({
|
|
|
141
144
|
return;
|
|
142
145
|
}
|
|
143
146
|
initializedRef.current = true;
|
|
144
|
-
if (apiKey) {
|
|
147
|
+
if (source?.apiKey) {
|
|
145
148
|
fetchAndSetup();
|
|
146
149
|
} else if (fixtures) {
|
|
147
150
|
setupInterceptor(fixtures);
|
|
@@ -157,18 +160,18 @@ function DemoKitProvider({
|
|
|
157
160
|
}, []);
|
|
158
161
|
(0, import_react2.useEffect)(() => {
|
|
159
162
|
if (!isHydrated || isLoading) return;
|
|
160
|
-
if (apiKey && remoteFixturesRef.current) {
|
|
163
|
+
if (source?.apiKey && remoteFixturesRef.current) {
|
|
161
164
|
const merged = { ...remoteFixturesRef.current, ...fixtures };
|
|
162
165
|
interceptorRef.current?.setFixtures(merged);
|
|
163
166
|
} else if (fixtures) {
|
|
164
167
|
interceptorRef.current?.setFixtures(fixtures);
|
|
165
168
|
}
|
|
166
|
-
}, [fixtures, isHydrated, isLoading,
|
|
169
|
+
}, [fixtures, isHydrated, isLoading, source]);
|
|
167
170
|
const enable = (0, import_react2.useCallback)(() => {
|
|
168
171
|
interceptorRef.current?.enable();
|
|
169
172
|
}, []);
|
|
170
173
|
const disable = (0, import_react2.useCallback)(() => {
|
|
171
|
-
interceptorRef.current?.disable();
|
|
174
|
+
return interceptorRef.current?.disable() ?? true;
|
|
172
175
|
}, []);
|
|
173
176
|
const toggle = (0, import_react2.useCallback)(() => {
|
|
174
177
|
interceptorRef.current?.toggle();
|
|
@@ -187,16 +190,17 @@ function DemoKitProvider({
|
|
|
187
190
|
return interceptorRef.current?.getSession() ?? null;
|
|
188
191
|
}, []);
|
|
189
192
|
const refetch = (0, import_react2.useCallback)(async () => {
|
|
190
|
-
if (!apiKey) {
|
|
191
|
-
console.warn("[DemoKit] refetch() called but no
|
|
193
|
+
if (!source?.apiKey) {
|
|
194
|
+
console.warn("[DemoKit] refetch() called but no source provided");
|
|
192
195
|
return;
|
|
193
196
|
}
|
|
194
197
|
await refetchFnRef.current?.();
|
|
195
|
-
}, [
|
|
198
|
+
}, [source]);
|
|
196
199
|
const value = (0, import_react2.useMemo)(
|
|
197
200
|
() => ({
|
|
198
201
|
isDemoMode,
|
|
199
202
|
isHydrated,
|
|
203
|
+
isPublicDemo,
|
|
200
204
|
isLoading,
|
|
201
205
|
remoteError,
|
|
202
206
|
remoteVersion,
|
|
@@ -211,6 +215,7 @@ function DemoKitProvider({
|
|
|
211
215
|
[
|
|
212
216
|
isDemoMode,
|
|
213
217
|
isHydrated,
|
|
218
|
+
isPublicDemo,
|
|
214
219
|
isLoading,
|
|
215
220
|
remoteError,
|
|
216
221
|
remoteVersion,
|
|
@@ -223,7 +228,7 @@ function DemoKitProvider({
|
|
|
223
228
|
refetch
|
|
224
229
|
]
|
|
225
230
|
);
|
|
226
|
-
if (isLoading && apiKey) {
|
|
231
|
+
if (isLoading && source?.apiKey) {
|
|
227
232
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DemoModeContext.Provider, { value, children: loadingFallback });
|
|
228
233
|
}
|
|
229
234
|
if (remoteError && errorFallback) {
|
|
@@ -233,6 +238,16 @@ function DemoKitProvider({
|
|
|
233
238
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DemoModeContext.Provider, { value, children });
|
|
234
239
|
}
|
|
235
240
|
|
|
241
|
+
// src/config.ts
|
|
242
|
+
function createRemoteSource(config) {
|
|
243
|
+
return {
|
|
244
|
+
timeout: 1e4,
|
|
245
|
+
retry: true,
|
|
246
|
+
maxRetries: 3,
|
|
247
|
+
...config
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
236
251
|
// src/hooks.ts
|
|
237
252
|
var import_react3 = require("react");
|
|
238
253
|
function useDemoMode() {
|
|
@@ -254,6 +269,29 @@ function useDemoSession() {
|
|
|
254
269
|
return useDemoMode().getSession();
|
|
255
270
|
}
|
|
256
271
|
|
|
272
|
+
// src/guard.ts
|
|
273
|
+
var import_react4 = require("react");
|
|
274
|
+
function useDemoGuard(options = {}) {
|
|
275
|
+
const { isDemoMode } = useDemoMode();
|
|
276
|
+
const { onBlocked } = options;
|
|
277
|
+
const guardMutation = (0, import_react4.useCallback)(
|
|
278
|
+
(action, actionName) => {
|
|
279
|
+
if (isDemoMode) {
|
|
280
|
+
const message = actionName ? `${actionName} (simulated in demo mode)` : "Action simulated in demo mode";
|
|
281
|
+
onBlocked?.(message);
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
action();
|
|
285
|
+
return true;
|
|
286
|
+
},
|
|
287
|
+
[isDemoMode, onBlocked]
|
|
288
|
+
);
|
|
289
|
+
return {
|
|
290
|
+
guardMutation,
|
|
291
|
+
isDemoMode
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
257
295
|
// src/powered-by.tsx
|
|
258
296
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
259
297
|
function ExternalLinkIcon({ size }) {
|
|
@@ -656,6 +694,8 @@ function DemoModeToggle({
|
|
|
656
694
|
DemoModeContext,
|
|
657
695
|
DemoModeToggle,
|
|
658
696
|
PoweredByBadge,
|
|
697
|
+
createRemoteSource,
|
|
698
|
+
useDemoGuard,
|
|
659
699
|
useDemoMode,
|
|
660
700
|
useDemoSession,
|
|
661
701
|
useIsDemoMode,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/context.ts","../src/hooks.ts","../src/powered-by.tsx","../src/banner.tsx","../src/toggle.tsx"],"sourcesContent":["/**\n * @demokit-ai/react\n *\n * React bindings for DemoKit - provider, hooks, and components.\n *\n * @example\n * import { DemoKitProvider, useDemoMode, DemoModeBanner } from '@demokit-ai/react'\n *\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * // In any component\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n * // ...\n * }\n *\n * @packageDocumentation\n */\n\n// Provider\nexport { DemoKitProvider } from './provider'\n\n// Hooks\nexport { useDemoMode, useIsDemoMode, useIsHydrated, useDemoSession } from './hooks'\n\n// Components\nexport { DemoModeBanner } from './banner'\nexport { DemoModeToggle } from './toggle'\nexport { PoweredByBadge } from './powered-by'\n\n// Context (for advanced use cases)\nexport { DemoModeContext } from './context'\n\n// Types\nexport type {\n DemoKitProviderProps,\n DemoModeContextValue,\n DemoModeBannerProps,\n} from './types'\n\n// Component types (from component files)\nexport type { DemoModeToggleProps } from './toggle'\nexport type { PoweredByBadgeProps } from './powered-by'\n\n// Re-export core types for convenience\nexport type {\n FixtureMap,\n FixtureHandler,\n RequestContext,\n SessionState,\n} from '@demokit-ai/core'\n","'use client'\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react'\nimport {\n createDemoInterceptor,\n fetchCloudFixtures,\n createRemoteFixtures,\n type DemoInterceptor,\n type SessionState,\n type FixtureMap,\n} from '@demokit-ai/core'\nimport { DemoModeContext } from './context'\nimport type { DemoKitProviderProps, DemoModeContextValue } from './types'\n\n/**\n * Provider component that enables demo mode functionality\n *\n * Wraps your app to provide demo mode state and controls.\n * Handles SSR hydration safely and persists state to localStorage.\n *\n * Supports two modes:\n * 1. **Local mode**: Pass `fixtures` prop with pattern handlers\n * 2. **Remote mode**: Pass `apiKey` to fetch from DemoKit Cloud\n *\n * @example Local mode\n * ```tsx\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * 'GET /api/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n * ```\n *\n * @example Remote mode (zero-config)\n * ```tsx\n * function App() {\n * return (\n * <DemoKitProvider\n * apiKey=\"dk_live_xxx\"\n * loadingFallback={<LoadingSpinner />}\n * >\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n * ```\n */\nexport function DemoKitProvider({\n children,\n fixtures,\n // Remote config\n apiKey,\n cloudUrl,\n timeout,\n retry,\n maxRetries,\n onRemoteLoad,\n onRemoteError,\n loadingFallback = null,\n errorFallback,\n // Standard props\n storageKey = 'demokit-mode',\n initialEnabled = false,\n onDemoModeChange,\n baseUrl,\n}: DemoKitProviderProps) {\n // Start with initialEnabled for SSR to avoid hydration mismatch\n const [isDemoMode, setIsDemoMode] = useState(initialEnabled)\n const [isHydrated, setIsHydrated] = useState(false)\n\n // Remote loading state\n const [isLoading, setIsLoading] = useState(!!apiKey)\n const [remoteError, setRemoteError] = useState<Error | null>(null)\n const [remoteVersion, setRemoteVersion] = useState<string | null>(null)\n\n // Keep a ref to the interceptor instance\n const interceptorRef = useRef<DemoInterceptor | null>(null)\n\n // Track if we've initialized\n const initializedRef = useRef(false)\n\n // Store loaded remote fixtures for refetch merging\n const remoteFixturesRef = useRef<FixtureMap | null>(null)\n\n // Store the refetch function for context\n const refetchFnRef = useRef<(() => Promise<void>) | null>(null)\n\n /**\n * Create and configure the demo interceptor\n */\n const setupInterceptor = useCallback(\n (mergedFixtures: FixtureMap) => {\n interceptorRef.current?.destroy()\n\n interceptorRef.current = createDemoInterceptor({\n fixtures: mergedFixtures,\n storageKey,\n initialEnabled,\n baseUrl,\n onEnable: () => {\n setIsDemoMode(true)\n onDemoModeChange?.(true)\n },\n onDisable: () => {\n setIsDemoMode(false)\n onDemoModeChange?.(false)\n },\n })\n\n // Sync state from storage after hydration\n const storedState = interceptorRef.current.isEnabled()\n setIsDemoMode(storedState)\n setIsHydrated(true)\n },\n [storageKey, initialEnabled, baseUrl, onDemoModeChange]\n )\n\n /**\n * Fetch fixtures from DemoKit Cloud and set up interceptor\n */\n const fetchAndSetup = useCallback(async () => {\n if (!apiKey) return\n\n setIsLoading(true)\n setRemoteError(null)\n\n try {\n const response = await fetchCloudFixtures({\n apiKey,\n cloudUrl,\n timeout,\n retry,\n maxRetries,\n onLoad: onRemoteLoad,\n onError: onRemoteError,\n })\n\n // Build fixtures from remote response with local overrides\n const remoteFixtures = createRemoteFixtures(response, fixtures)\n remoteFixturesRef.current = remoteFixtures\n\n setRemoteVersion(response.version)\n setupInterceptor(remoteFixtures)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n setRemoteError(err)\n onRemoteError?.(err)\n\n // If we have local fixtures, still set up with those\n if (fixtures && Object.keys(fixtures).length > 0) {\n setupInterceptor(fixtures)\n } else {\n setIsHydrated(true)\n }\n } finally {\n setIsLoading(false)\n }\n }, [\n apiKey,\n cloudUrl,\n timeout,\n retry,\n maxRetries,\n fixtures,\n onRemoteLoad,\n onRemoteError,\n setupInterceptor,\n ])\n\n // Store refetch function in ref for context access\n refetchFnRef.current = fetchAndSetup\n\n // Initialize on mount\n useEffect(() => {\n if (initializedRef.current) {\n return\n }\n initializedRef.current = true\n\n if (apiKey) {\n // Remote mode: fetch from cloud\n fetchAndSetup()\n } else if (fixtures) {\n // Local mode: use provided fixtures\n setupInterceptor(fixtures)\n } else {\n // No fixtures at all - just mark as hydrated\n setIsHydrated(true)\n setIsLoading(false)\n }\n\n return () => {\n interceptorRef.current?.destroy()\n interceptorRef.current = null\n initializedRef.current = false\n }\n }, []) // Empty deps - only run once on mount\n\n // Update fixtures if they change (local mode or overrides)\n useEffect(() => {\n if (!isHydrated || isLoading) return\n\n if (apiKey && remoteFixturesRef.current) {\n // Remote mode: merge new local overrides with cached remote fixtures\n const merged = { ...remoteFixturesRef.current, ...fixtures }\n interceptorRef.current?.setFixtures(merged)\n } else if (fixtures) {\n // Local mode: update fixtures\n interceptorRef.current?.setFixtures(fixtures)\n }\n }, [fixtures, isHydrated, isLoading, apiKey])\n\n const enable = useCallback(() => {\n interceptorRef.current?.enable()\n }, [])\n\n const disable = useCallback(() => {\n interceptorRef.current?.disable()\n }, [])\n\n const toggle = useCallback(() => {\n interceptorRef.current?.toggle()\n }, [])\n\n const setDemoMode = useCallback((enabled: boolean) => {\n if (enabled) {\n interceptorRef.current?.enable()\n } else {\n interceptorRef.current?.disable()\n }\n }, [])\n\n const resetSession = useCallback(() => {\n interceptorRef.current?.resetSession()\n }, [])\n\n const getSession = useCallback((): SessionState | null => {\n return interceptorRef.current?.getSession() ?? null\n }, [])\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!apiKey) {\n console.warn('[DemoKit] refetch() called but no apiKey provided')\n return\n }\n await refetchFnRef.current?.()\n }, [apiKey])\n\n const value = useMemo<DemoModeContextValue>(\n () => ({\n isDemoMode,\n isHydrated,\n isLoading,\n remoteError,\n remoteVersion,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n refetch,\n }),\n [\n isDemoMode,\n isHydrated,\n isLoading,\n remoteError,\n remoteVersion,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n refetch,\n ]\n )\n\n // Render loading state\n if (isLoading && apiKey) {\n return (\n <DemoModeContext.Provider value={value}>\n {loadingFallback}\n </DemoModeContext.Provider>\n )\n }\n\n // Render error state\n if (remoteError && errorFallback) {\n const errorContent =\n typeof errorFallback === 'function'\n ? errorFallback(remoteError)\n : errorFallback\n\n return (\n <DemoModeContext.Provider value={value}>\n {errorContent}\n </DemoModeContext.Provider>\n )\n }\n\n return (\n <DemoModeContext.Provider value={value}>{children}</DemoModeContext.Provider>\n )\n}\n","import { createContext } from 'react'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * React context for demo mode state\n * @internal\n */\nexport const DemoModeContext = createContext<DemoModeContextValue | undefined>(undefined)\n\nDemoModeContext.displayName = 'DemoModeContext'\n","'use client'\n\nimport { useContext } from 'react'\nimport { DemoModeContext } from './context'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * Hook to access demo mode state and controls\n *\n * @returns Demo mode context value with state and control methods\n * @throws Error if used outside of DemoKitProvider\n *\n * @example\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n *\n * // Wait for hydration before rendering demo-dependent UI\n * if (!isHydrated) {\n * return <Loading />\n * }\n *\n * return (\n * <div>\n * <p>Demo mode: {isDemoMode ? 'ON' : 'OFF'}</p>\n * <button onClick={toggle}>Toggle</button>\n * </div>\n * )\n * }\n */\nexport function useDemoMode(): DemoModeContextValue {\n const context = useContext(DemoModeContext)\n\n if (context === undefined) {\n throw new Error(\n 'useDemoMode must be used within a DemoKitProvider. ' +\n 'Make sure to wrap your app with <DemoKitProvider>.'\n )\n }\n\n return context\n}\n\n/**\n * Hook to check if demo mode is enabled\n * Shorthand for useDemoMode().isDemoMode\n *\n * @returns Whether demo mode is enabled\n */\nexport function useIsDemoMode(): boolean {\n return useDemoMode().isDemoMode\n}\n\n/**\n * Hook to check if the component has hydrated\n * Shorthand for useDemoMode().isHydrated\n *\n * @returns Whether the component has hydrated\n */\nexport function useIsHydrated(): boolean {\n return useDemoMode().isHydrated\n}\n\n/**\n * Hook to access the session state\n * Shorthand for useDemoMode().getSession()\n *\n * @returns The session state, or null if not yet initialized\n *\n * @example\n * function MyComponent() {\n * const session = useDemoSession()\n *\n * const cart = session?.get<CartItem[]>('cart') || []\n * const addToCart = (item: CartItem) => {\n * session?.set('cart', [...cart, item])\n * }\n *\n * return <CartView items={cart} onAdd={addToCart} />\n * }\n */\nexport function useDemoSession() {\n return useDemoMode().getSession()\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\n\n/**\n * Props for the PoweredByBadge component\n */\nexport interface PoweredByBadgeProps {\n /**\n * URL to link to when clicked\n * @default 'https://demokit.ai'\n */\n url?: string\n\n /**\n * Visual variant for light/dark backgrounds\n * @default 'auto'\n */\n variant?: 'light' | 'dark' | 'auto'\n\n /**\n * Size of the badge\n * @default 'sm'\n */\n size?: 'xs' | 'sm' | 'md'\n\n /**\n * Additional CSS class name\n */\n className?: string\n\n /**\n * Custom styles\n */\n style?: CSSProperties\n}\n\n/**\n * External link icon\n */\nfunction ExternalLinkIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n <polyline points=\"15 3 21 3 21 9\" />\n <line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\" />\n </svg>\n )\n}\n\n/**\n * Size configurations\n */\nconst sizeConfig = {\n xs: {\n fontSize: '10px',\n padding: '2px 6px',\n gap: '3px',\n iconSize: 8,\n },\n sm: {\n fontSize: '11px',\n padding: '3px 8px',\n gap: '4px',\n iconSize: 10,\n },\n md: {\n fontSize: '12px',\n padding: '4px 10px',\n gap: '5px',\n iconSize: 12,\n },\n}\n\n/**\n * Variant configurations\n */\nconst variantStyles = {\n light: {\n color: 'rgba(120, 53, 15, 0.7)',\n hoverColor: 'rgba(120, 53, 15, 0.9)',\n backgroundColor: 'transparent',\n hoverBackgroundColor: 'rgba(217, 119, 6, 0.08)',\n },\n dark: {\n color: 'rgba(255, 255, 255, 0.6)',\n hoverColor: 'rgba(255, 255, 255, 0.9)',\n backgroundColor: 'transparent',\n hoverBackgroundColor: 'rgba(255, 255, 255, 0.1)',\n },\n}\n\n/**\n * A \"Powered by DemoKit\" badge that links to demokit.ai\n *\n * This badge is shown by default for OSS users and cannot be hidden\n * without a valid DemoKit Cloud paid plan.\n *\n * @example\n * <PoweredByBadge />\n *\n * @example With dark theme\n * <PoweredByBadge variant=\"dark\" />\n *\n * @example Small size\n * <PoweredByBadge size=\"xs\" />\n */\nexport function PoweredByBadge({\n url = 'https://demokit.ai',\n variant = 'light',\n size = 'sm',\n className = '',\n style,\n}: PoweredByBadgeProps) {\n const config = sizeConfig[size]\n const colors = variantStyles[variant === 'auto' ? 'light' : variant]\n\n const baseStyles: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: config.gap,\n fontSize: config.fontSize,\n padding: config.padding,\n color: colors.color,\n textDecoration: 'none',\n borderRadius: '4px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n fontWeight: 500,\n transition: 'color 0.15s ease, background-color 0.15s ease',\n whiteSpace: 'nowrap',\n ...style,\n }\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={`demokit-powered-by ${className}`.trim()}\n style={baseStyles}\n onMouseOver={(e) => {\n e.currentTarget.style.color = colors.hoverColor\n e.currentTarget.style.backgroundColor = colors.hoverBackgroundColor\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.color = colors.color\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n >\n <span>Powered by DemoKit</span>\n <ExternalLinkIcon size={config.iconSize} />\n </a>\n )\n}\n","'use client'\n\nimport { useDemoMode } from './hooks'\nimport { PoweredByBadge } from './powered-by'\nimport type { DemoModeBannerProps } from './types'\n\n/**\n * Eye icon SVG component\n */\nfunction EyeIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\n/**\n * Default styles for the banner\n */\nconst defaultStyles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#fef3c7',\n borderBottom: '1px solid rgba(217, 119, 6, 0.2)',\n fontSize: '14px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n } as React.CSSProperties,\n content: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n } as React.CSSProperties,\n icon: {\n color: '#d97706',\n flexShrink: 0,\n } as React.CSSProperties,\n label: {\n fontWeight: 600,\n color: '#78350f',\n } as React.CSSProperties,\n description: {\n color: 'rgba(120, 53, 15, 0.7)',\n fontSize: '12px',\n } as React.CSSProperties,\n button: {\n padding: '4px 12px',\n fontSize: '14px',\n backgroundColor: 'transparent',\n border: '1px solid rgba(217, 119, 6, 0.3)',\n borderRadius: '4px',\n cursor: 'pointer',\n color: '#78350f',\n fontFamily: 'inherit',\n transition: 'background-color 0.15s ease',\n } as React.CSSProperties,\n}\n\n/**\n * A ready-to-use banner component that shows when demo mode is active\n *\n * Displays a prominent amber banner with a label, description, and exit button.\n * Automatically hides when demo mode is disabled or before hydration.\n *\n * @example\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * @example Custom labels\n * <DemoModeBanner\n * demoLabel=\"Preview Mode\"\n * description=\"You're viewing sample data\"\n * exitLabel=\"Exit Preview\"\n * />\n */\nexport function DemoModeBanner({\n className = '',\n exitLabel = 'Exit Demo Mode',\n demoLabel = 'Demo Mode Active',\n description = 'Changes are simulated and not saved',\n showIcon = true,\n showPoweredBy = true,\n poweredByUrl = 'https://demokit.ai',\n style,\n onExit,\n}: DemoModeBannerProps) {\n const { isDemoMode, isHydrated, disable } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated || !isDemoMode) {\n return null\n }\n\n const handleExit = () => {\n if (onExit) {\n onExit()\n } else {\n disable()\n }\n }\n\n // For OSS users, branding is always shown (enforced server-side in cloud)\n // The prop is only respected for paid cloud users\n const effectiveShowPoweredBy = showPoweredBy\n\n return (\n <div\n className={`demokit-banner ${className}`.trim()}\n style={{ ...defaultStyles.container, flexDirection: 'column', gap: '4px', ...style }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>\n <div style={defaultStyles.content}>\n {showIcon && (\n <span style={defaultStyles.icon}>\n <EyeIcon />\n </span>\n )}\n <span style={defaultStyles.label}>{demoLabel}</span>\n {description && <span style={defaultStyles.description}>{description}</span>}\n </div>\n <button\n onClick={handleExit}\n style={defaultStyles.button}\n onMouseOver={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(217, 119, 6, 0.1)'\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n type=\"button\"\n >\n {exitLabel}\n </button>\n </div>\n {effectiveShowPoweredBy && (\n <div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>\n <PoweredByBadge url={poweredByUrl} size=\"xs\" />\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\nimport { useDemoMode } from './hooks'\nimport { PoweredByBadge } from './powered-by'\n\n/**\n * Props for the DemoModeToggle component\n */\nexport interface DemoModeToggleProps {\n /**\n * Show \"Demo Mode\" label next to the toggle\n * @default true\n */\n showLabel?: boolean\n\n /**\n * Label text to display\n * @default 'Demo Mode'\n */\n label?: string\n\n /**\n * Show \"Powered by DemoKit\" branding\n * Note: For OSS users, this is always true regardless of the prop value.\n * Only paid DemoKit Cloud users can hide the branding.\n * @default true\n */\n showPoweredBy?: boolean\n\n /**\n * URL for the \"Powered by\" link\n * @default 'https://demokit.ai'\n */\n poweredByUrl?: string\n\n /**\n * Position of the toggle\n * - 'inline': Renders where placed in the component tree\n * - 'floating': Fixed position, can be moved around\n * - 'corner': Fixed to bottom-right corner\n * @default 'inline'\n */\n position?: 'inline' | 'floating' | 'corner'\n\n /**\n * Size of the toggle\n * @default 'md'\n */\n size?: 'sm' | 'md' | 'lg'\n\n /**\n * Additional CSS class name\n */\n className?: string\n\n /**\n * Custom styles\n */\n style?: CSSProperties\n\n /**\n * Callback when toggle state changes\n */\n onChange?: (enabled: boolean) => void\n}\n\n/**\n * Size configurations for the toggle\n */\nconst sizeConfig = {\n sm: {\n trackWidth: 36,\n trackHeight: 20,\n thumbSize: 16,\n thumbOffset: 2,\n fontSize: '12px',\n padding: '8px 12px',\n gap: '8px',\n },\n md: {\n trackWidth: 44,\n trackHeight: 24,\n thumbSize: 20,\n thumbOffset: 2,\n fontSize: '14px',\n padding: '12px 16px',\n gap: '10px',\n },\n lg: {\n trackWidth: 52,\n trackHeight: 28,\n thumbSize: 24,\n thumbOffset: 2,\n fontSize: '16px',\n padding: '16px 20px',\n gap: '12px',\n },\n}\n\n/**\n * Position configurations\n */\nconst positionStyles: Record<string, CSSProperties> = {\n inline: {},\n floating: {\n position: 'fixed',\n zIndex: 9999,\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n },\n corner: {\n position: 'fixed',\n bottom: '20px',\n right: '20px',\n zIndex: 9999,\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n },\n}\n\n/**\n * Default styles\n */\nconst defaultStyles = {\n container: {\n display: 'inline-flex',\n flexDirection: 'column' as const,\n alignItems: 'flex-start',\n gap: '4px',\n backgroundColor: '#ffffff',\n border: '1px solid rgba(0, 0, 0, 0.1)',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n },\n row: {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n },\n label: {\n fontWeight: 500,\n color: '#1f2937',\n userSelect: 'none' as const,\n },\n track: {\n position: 'relative' as const,\n borderRadius: '9999px',\n cursor: 'pointer',\n transition: 'background-color 0.2s ease',\n flexShrink: 0,\n },\n trackOff: {\n backgroundColor: '#d1d5db',\n },\n trackOn: {\n backgroundColor: '#d97706',\n },\n thumb: {\n position: 'absolute' as const,\n top: '50%',\n transform: 'translateY(-50%)',\n backgroundColor: '#ffffff',\n borderRadius: '50%',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n transition: 'left 0.2s ease',\n },\n poweredByContainer: {\n display: 'flex',\n justifyContent: 'flex-end',\n width: '100%',\n },\n}\n\n/**\n * A toggle switch component for enabling/disabling demo mode\n *\n * Supports inline placement or fixed positioning (floating/corner).\n * Includes optional \"Powered by DemoKit\" branding that is always shown for OSS users.\n *\n * @example Basic inline usage\n * <DemoModeToggle />\n *\n * @example Corner position (floating)\n * <DemoModeToggle position=\"corner\" />\n *\n * @example Without label\n * <DemoModeToggle showLabel={false} />\n *\n * @example With custom label and callback\n * <DemoModeToggle\n * label=\"Preview Mode\"\n * onChange={(enabled) => console.log('Demo mode:', enabled)}\n * />\n */\nexport function DemoModeToggle({\n showLabel = true,\n label = 'Demo Mode',\n showPoweredBy = true,\n poweredByUrl = 'https://demokit.ai',\n position = 'inline',\n size = 'md',\n className = '',\n style,\n onChange,\n}: DemoModeToggleProps) {\n const { isDemoMode, isHydrated, toggle } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated) {\n return null\n }\n\n const config = sizeConfig[size]\n const posStyle = positionStyles[position]\n\n const handleToggle = () => {\n toggle()\n onChange?.(!isDemoMode)\n }\n\n const thumbLeft = isDemoMode\n ? config.trackWidth - config.thumbSize - config.thumbOffset\n : config.thumbOffset\n\n // For OSS users, branding is always shown (enforced server-side in cloud)\n // The prop is only respected for paid cloud users\n const effectiveShowPoweredBy = showPoweredBy\n\n return (\n <div\n className={`demokit-toggle ${className}`.trim()}\n style={{\n ...defaultStyles.container,\n padding: config.padding,\n ...posStyle,\n ...style,\n }}\n role=\"group\"\n aria-label=\"Demo mode toggle\"\n >\n <div style={{ ...defaultStyles.row, gap: config.gap }}>\n {showLabel && (\n <span style={{ ...defaultStyles.label, fontSize: config.fontSize }}>{label}</span>\n )}\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isDemoMode}\n aria-label={`${label}: ${isDemoMode ? 'On' : 'Off'}`}\n onClick={handleToggle}\n style={{\n ...defaultStyles.track,\n ...(isDemoMode ? defaultStyles.trackOn : defaultStyles.trackOff),\n width: config.trackWidth,\n height: config.trackHeight,\n border: 'none',\n padding: 0,\n }}\n >\n <span\n style={{\n ...defaultStyles.thumb,\n width: config.thumbSize,\n height: config.thumbSize,\n left: thumbLeft,\n }}\n />\n </button>\n </div>\n {effectiveShowPoweredBy && (\n <div style={defaultStyles.poweredByContainer}>\n <PoweredByBadge url={poweredByUrl} size=\"xs\" />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","sizeConfig","defaultStyles"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/provider.tsx","../src/context.ts","../src/config.ts","../src/hooks.ts","../src/guard.ts","../src/powered-by.tsx","../src/banner.tsx","../src/toggle.tsx"],"sourcesContent":["/**\n * @demokit-ai/react\n *\n * React bindings for DemoKit - provider, hooks, and components.\n *\n * @example\n * import { DemoKitProvider, useDemoMode, DemoModeBanner } from '@demokit-ai/react'\n *\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * // In any component\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n * // ...\n * }\n *\n * @packageDocumentation\n */\n\n// Provider\nexport { DemoKitProvider } from './provider'\n\n// Config helpers\nexport { createRemoteSource } from './config'\n\n// Hooks\nexport { useDemoMode, useIsDemoMode, useIsHydrated, useDemoSession } from './hooks'\nexport { useDemoGuard } from './guard'\n\n// Components\nexport { DemoModeBanner } from './banner'\nexport { DemoModeToggle } from './toggle'\nexport { PoweredByBadge } from './powered-by'\n\n// Context (for advanced use cases)\nexport { DemoModeContext } from './context'\n\n// Types\nexport type {\n DemoKitProviderProps,\n DemoModeContextValue,\n DemoModeBannerProps,\n} from './types'\n\n// Guard types\nexport type { UseDemoGuardOptions, DemoGuardReturn } from './guard'\n\n// Component types (from component files)\nexport type { DemoModeToggleProps } from './toggle'\nexport type { PoweredByBadgeProps } from './powered-by'\n\n// Re-export core types for convenience\nexport type {\n FixtureMap,\n FixtureHandler,\n RequestContext,\n SessionState,\n RemoteConfig,\n DetectionConfig,\n MutationInterceptedContext,\n} from '@demokit-ai/core'\n","'use client'\n\nimport { useState, useEffect, useCallback, useMemo, useRef } from 'react'\nimport {\n createDemoInterceptor,\n fetchCloudFixtures,\n createRemoteFixtures,\n type DemoInterceptor,\n type SessionState,\n type FixtureMap,\n} from '@demokit-ai/core'\nimport { DemoModeContext } from './context'\nimport type { DemoKitProviderProps, DemoModeContextValue } from './types'\n\n/**\n * Provider component that enables demo mode functionality\n *\n * Wraps your app to provide demo mode state and controls.\n * Handles SSR hydration safely and persists state to localStorage.\n *\n * Supports two modes:\n * 1. **Local mode**: Pass `fixtures` prop with pattern handlers\n * 2. **Remote mode**: Pass `source` to fetch from DemoKit Cloud\n *\n * @example Local mode\n * ```tsx\n * const fixtures = {\n * 'GET /api/users': () => [{ id: '1', name: 'Demo User' }],\n * 'GET /api/users/:id': ({ params }) => ({ id: params.id, name: 'Demo User' }),\n * }\n *\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n * ```\n *\n * @example Remote mode\n * ```tsx\n * import { createRemoteSource } from '@demokit-ai/react'\n *\n * const source = createRemoteSource({\n * apiUrl: process.env.NEXT_PUBLIC_DEMOKIT_API_URL!,\n * apiKey: process.env.NEXT_PUBLIC_DEMOKIT_API_KEY!,\n * })\n *\n * function App() {\n * return (\n * <DemoKitProvider\n * source={source}\n * loadingFallback={<LoadingSpinner />}\n * >\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n * ```\n */\nexport function DemoKitProvider({\n children,\n fixtures,\n // Remote config\n source,\n onRemoteLoad,\n onRemoteError,\n loadingFallback = null,\n errorFallback,\n // Standard props\n storageKey = 'demokit-mode',\n initialEnabled = false,\n onDemoModeChange,\n baseUrl,\n // Detection & guards\n detection,\n canDisable,\n onMutationIntercepted,\n}: DemoKitProviderProps) {\n // Start with initialEnabled for SSR to avoid hydration mismatch\n const [isDemoMode, setIsDemoMode] = useState(initialEnabled)\n const [isHydrated, setIsHydrated] = useState(false)\n const [isPublicDemo, setIsPublicDemo] = useState(false)\n\n // Remote loading state\n const [isLoading, setIsLoading] = useState(!!source?.apiKey)\n const [remoteError, setRemoteError] = useState<Error | null>(null)\n const [remoteVersion, setRemoteVersion] = useState<string | null>(null)\n\n // Keep a ref to the interceptor instance\n const interceptorRef = useRef<DemoInterceptor | null>(null)\n\n // Track if we've initialized\n const initializedRef = useRef(false)\n\n // Store loaded remote fixtures for refetch merging\n const remoteFixturesRef = useRef<FixtureMap | null>(null)\n\n // Store the refetch function for context\n const refetchFnRef = useRef<(() => Promise<void>) | null>(null)\n\n /**\n * Create and configure the demo interceptor\n */\n const setupInterceptor = useCallback(\n (mergedFixtures: FixtureMap) => {\n interceptorRef.current?.destroy()\n\n interceptorRef.current = createDemoInterceptor({\n fixtures: mergedFixtures,\n storageKey,\n initialEnabled,\n baseUrl,\n detection,\n canDisable,\n onMutationIntercepted,\n onEnable: () => {\n setIsDemoMode(true)\n onDemoModeChange?.(true)\n },\n onDisable: () => {\n setIsDemoMode(false)\n onDemoModeChange?.(false)\n },\n })\n\n // Sync state from storage after hydration\n const storedState = interceptorRef.current.isEnabled()\n setIsDemoMode(storedState)\n setIsPublicDemo(interceptorRef.current.isPublicDemo())\n setIsHydrated(true)\n },\n [storageKey, initialEnabled, baseUrl, onDemoModeChange, detection, canDisable, onMutationIntercepted]\n )\n\n /**\n * Fetch fixtures from DemoKit Cloud and set up interceptor\n */\n const fetchAndSetup = useCallback(async () => {\n if (!source?.apiKey) return\n\n setIsLoading(true)\n setRemoteError(null)\n\n try {\n const response = await fetchCloudFixtures({\n apiKey: source.apiKey,\n apiUrl: source.apiUrl,\n timeout: source.timeout,\n retry: source.retry,\n maxRetries: source.maxRetries,\n onLoad: onRemoteLoad,\n onError: onRemoteError,\n })\n\n // Build fixtures from remote response with local overrides\n const remoteFixtures = createRemoteFixtures(response, fixtures)\n remoteFixturesRef.current = remoteFixtures\n\n setRemoteVersion(response.version)\n setupInterceptor(remoteFixtures)\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error))\n setRemoteError(err)\n onRemoteError?.(err)\n\n // If we have local fixtures, still set up with those\n if (fixtures && Object.keys(fixtures).length > 0) {\n setupInterceptor(fixtures)\n } else {\n setIsHydrated(true)\n }\n } finally {\n setIsLoading(false)\n }\n }, [\n source,\n fixtures,\n onRemoteLoad,\n onRemoteError,\n setupInterceptor,\n ])\n\n // Store refetch function in ref for context access\n refetchFnRef.current = fetchAndSetup\n\n // Initialize on mount\n useEffect(() => {\n if (initializedRef.current) {\n return\n }\n initializedRef.current = true\n\n if (source?.apiKey) {\n // Remote mode: fetch from cloud\n fetchAndSetup()\n } else if (fixtures) {\n // Local mode: use provided fixtures\n setupInterceptor(fixtures)\n } else {\n // No fixtures at all - just mark as hydrated\n setIsHydrated(true)\n setIsLoading(false)\n }\n\n return () => {\n interceptorRef.current?.destroy()\n interceptorRef.current = null\n initializedRef.current = false\n }\n }, []) // Empty deps - only run once on mount\n\n // Update fixtures if they change (local mode or overrides)\n useEffect(() => {\n if (!isHydrated || isLoading) return\n\n if (source?.apiKey && remoteFixturesRef.current) {\n // Remote mode: merge new local overrides with cached remote fixtures\n const merged = { ...remoteFixturesRef.current, ...fixtures }\n interceptorRef.current?.setFixtures(merged)\n } else if (fixtures) {\n // Local mode: update fixtures\n interceptorRef.current?.setFixtures(fixtures)\n }\n }, [fixtures, isHydrated, isLoading, source])\n\n const enable = useCallback(() => {\n interceptorRef.current?.enable()\n }, [])\n\n const disable = useCallback((): boolean | string => {\n return interceptorRef.current?.disable() ?? true\n }, [])\n\n const toggle = useCallback(() => {\n interceptorRef.current?.toggle()\n }, [])\n\n const setDemoMode = useCallback((enabled: boolean) => {\n if (enabled) {\n interceptorRef.current?.enable()\n } else {\n interceptorRef.current?.disable()\n }\n }, [])\n\n const resetSession = useCallback(() => {\n interceptorRef.current?.resetSession()\n }, [])\n\n const getSession = useCallback((): SessionState | null => {\n return interceptorRef.current?.getSession() ?? null\n }, [])\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!source?.apiKey) {\n console.warn('[DemoKit] refetch() called but no source provided')\n return\n }\n await refetchFnRef.current?.()\n }, [source])\n\n const value = useMemo<DemoModeContextValue>(\n () => ({\n isDemoMode,\n isHydrated,\n isPublicDemo,\n isLoading,\n remoteError,\n remoteVersion,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n refetch,\n }),\n [\n isDemoMode,\n isHydrated,\n isPublicDemo,\n isLoading,\n remoteError,\n remoteVersion,\n enable,\n disable,\n toggle,\n setDemoMode,\n resetSession,\n getSession,\n refetch,\n ]\n )\n\n // Render loading state\n if (isLoading && source?.apiKey) {\n return (\n <DemoModeContext.Provider value={value}>\n {loadingFallback}\n </DemoModeContext.Provider>\n )\n }\n\n // Render error state\n if (remoteError && errorFallback) {\n const errorContent =\n typeof errorFallback === 'function'\n ? errorFallback(remoteError)\n : errorFallback\n\n return (\n <DemoModeContext.Provider value={value}>\n {errorContent}\n </DemoModeContext.Provider>\n )\n }\n\n return (\n <DemoModeContext.Provider value={value}>{children}</DemoModeContext.Provider>\n )\n}\n","import { createContext } from 'react'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * React context for demo mode state\n * @internal\n */\nexport const DemoModeContext = createContext<DemoModeContextValue | undefined>(undefined)\n\nDemoModeContext.displayName = 'DemoModeContext'\n","import type { RemoteConfig } from '@demokit-ai/core'\n\n/**\n * Create a remote source configuration for fetching fixtures from DemoKit Cloud\n *\n * @example\n * ```tsx\n * import { createRemoteSource } from '@demokit-ai/react'\n *\n * const source = createRemoteSource({\n * apiUrl: process.env.NEXT_PUBLIC_DEMOKIT_API_URL!,\n * apiKey: process.env.NEXT_PUBLIC_DEMOKIT_API_KEY!,\n * })\n *\n * <DemoKitProvider source={source}>\n * {children}\n * </DemoKitProvider>\n * ```\n */\nexport function createRemoteSource(config: RemoteConfig): RemoteConfig {\n return {\n timeout: 10000,\n retry: true,\n maxRetries: 3,\n ...config,\n }\n}\n","'use client'\n\nimport { useContext } from 'react'\nimport { DemoModeContext } from './context'\nimport type { DemoModeContextValue } from './types'\n\n/**\n * Hook to access demo mode state and controls\n *\n * @returns Demo mode context value with state and control methods\n * @throws Error if used outside of DemoKitProvider\n *\n * @example\n * function MyComponent() {\n * const { isDemoMode, isHydrated, toggle } = useDemoMode()\n *\n * // Wait for hydration before rendering demo-dependent UI\n * if (!isHydrated) {\n * return <Loading />\n * }\n *\n * return (\n * <div>\n * <p>Demo mode: {isDemoMode ? 'ON' : 'OFF'}</p>\n * <button onClick={toggle}>Toggle</button>\n * </div>\n * )\n * }\n */\nexport function useDemoMode(): DemoModeContextValue {\n const context = useContext(DemoModeContext)\n\n if (context === undefined) {\n throw new Error(\n 'useDemoMode must be used within a DemoKitProvider. ' +\n 'Make sure to wrap your app with <DemoKitProvider>.'\n )\n }\n\n return context\n}\n\n/**\n * Hook to check if demo mode is enabled\n * Shorthand for useDemoMode().isDemoMode\n *\n * @returns Whether demo mode is enabled\n */\nexport function useIsDemoMode(): boolean {\n return useDemoMode().isDemoMode\n}\n\n/**\n * Hook to check if the component has hydrated\n * Shorthand for useDemoMode().isHydrated\n *\n * @returns Whether the component has hydrated\n */\nexport function useIsHydrated(): boolean {\n return useDemoMode().isHydrated\n}\n\n/**\n * Hook to access the session state\n * Shorthand for useDemoMode().getSession()\n *\n * @returns The session state, or null if not yet initialized\n *\n * @example\n * function MyComponent() {\n * const session = useDemoSession()\n *\n * const cart = session?.get<CartItem[]>('cart') || []\n * const addToCart = (item: CartItem) => {\n * session?.set('cart', [...cart, item])\n * }\n *\n * return <CartView items={cart} onAdd={addToCart} />\n * }\n */\nexport function useDemoSession() {\n return useDemoMode().getSession()\n}\n","'use client'\n\nimport { useCallback } from 'react'\nimport { useDemoMode } from './hooks'\n\nexport interface UseDemoGuardOptions {\n /**\n * Called when a mutation is blocked in demo mode.\n * Use this to show a toast or notification to the user.\n * @param message - The action name or a default message\n */\n onBlocked?: (message: string) => void\n}\n\nexport interface DemoGuardReturn {\n /**\n * Whether demo mode is currently active\n */\n isDemoMode: boolean\n\n /**\n * Wraps a mutation action — prevents execution in demo mode.\n * When blocked, calls `onBlocked` with the action name.\n *\n * @param action - The mutation function to execute (only runs if NOT in demo mode)\n * @param actionName - Human-readable name (e.g., \"Changes saved\")\n * @returns `true` if the action was executed, `false` if blocked\n *\n * @example\n * ```tsx\n * const { guardMutation } = useDemoGuard({\n * onBlocked: (msg) => toast.success(msg)\n * })\n *\n * guardMutation(() => sdk.deleteItem(id), 'Item deleted')\n * ```\n */\n guardMutation: (\n action: () => void | Promise<void>,\n actionName?: string\n ) => boolean\n}\n\n/**\n * Hook that prevents mutations from executing in demo mode.\n *\n * Instead of letting mutations hit the (intercepted) API and trigger\n * side effects like optimistic updates and onSuccess handlers, this\n * hook blocks the mutation entirely and optionally notifies the user.\n *\n * @example\n * ```tsx\n * import { useDemoGuard } from '@demokit-ai/react'\n * import { toast } from 'sonner'\n *\n * function MyComponent() {\n * const { guardMutation } = useDemoGuard({\n * onBlocked: (msg) => toast.success(msg, {\n * description: 'Changes are not saved. Exit demo mode to make real changes.',\n * })\n * })\n *\n * const handleDelete = () => {\n * guardMutation(() => sdk.deleteItem(id), 'Item deleted')\n * }\n * }\n * ```\n */\nexport function useDemoGuard(options: UseDemoGuardOptions = {}): DemoGuardReturn {\n const { isDemoMode } = useDemoMode()\n const { onBlocked } = options\n\n const guardMutation = useCallback(\n (action: () => void | Promise<void>, actionName?: string): boolean => {\n if (isDemoMode) {\n const message = actionName\n ? `${actionName} (simulated in demo mode)`\n : 'Action simulated in demo mode'\n\n onBlocked?.(message)\n return false\n }\n\n action()\n return true\n },\n [isDemoMode, onBlocked]\n )\n\n return {\n guardMutation,\n isDemoMode,\n }\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\n\n/**\n * Props for the PoweredByBadge component\n */\nexport interface PoweredByBadgeProps {\n /**\n * URL to link to when clicked\n * @default 'https://demokit.ai'\n */\n url?: string\n\n /**\n * Visual variant for light/dark backgrounds\n * @default 'auto'\n */\n variant?: 'light' | 'dark' | 'auto'\n\n /**\n * Size of the badge\n * @default 'sm'\n */\n size?: 'xs' | 'sm' | 'md'\n\n /**\n * Additional CSS class name\n */\n className?: string\n\n /**\n * Custom styles\n */\n style?: CSSProperties\n}\n\n/**\n * External link icon\n */\nfunction ExternalLinkIcon({ size }: { size: number }) {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6\" />\n <polyline points=\"15 3 21 3 21 9\" />\n <line x1=\"10\" y1=\"14\" x2=\"21\" y2=\"3\" />\n </svg>\n )\n}\n\n/**\n * Size configurations\n */\nconst sizeConfig = {\n xs: {\n fontSize: '10px',\n padding: '2px 6px',\n gap: '3px',\n iconSize: 8,\n },\n sm: {\n fontSize: '11px',\n padding: '3px 8px',\n gap: '4px',\n iconSize: 10,\n },\n md: {\n fontSize: '12px',\n padding: '4px 10px',\n gap: '5px',\n iconSize: 12,\n },\n}\n\n/**\n * Variant configurations\n */\nconst variantStyles = {\n light: {\n color: 'rgba(120, 53, 15, 0.7)',\n hoverColor: 'rgba(120, 53, 15, 0.9)',\n backgroundColor: 'transparent',\n hoverBackgroundColor: 'rgba(217, 119, 6, 0.08)',\n },\n dark: {\n color: 'rgba(255, 255, 255, 0.6)',\n hoverColor: 'rgba(255, 255, 255, 0.9)',\n backgroundColor: 'transparent',\n hoverBackgroundColor: 'rgba(255, 255, 255, 0.1)',\n },\n}\n\n/**\n * A \"Powered by DemoKit\" badge that links to demokit.ai\n *\n * This badge is shown by default for OSS users and cannot be hidden\n * without a valid DemoKit Cloud paid plan.\n *\n * @example\n * <PoweredByBadge />\n *\n * @example With dark theme\n * <PoweredByBadge variant=\"dark\" />\n *\n * @example Small size\n * <PoweredByBadge size=\"xs\" />\n */\nexport function PoweredByBadge({\n url = 'https://demokit.ai',\n variant = 'light',\n size = 'sm',\n className = '',\n style,\n}: PoweredByBadgeProps) {\n const config = sizeConfig[size]\n const colors = variantStyles[variant === 'auto' ? 'light' : variant]\n\n const baseStyles: CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n gap: config.gap,\n fontSize: config.fontSize,\n padding: config.padding,\n color: colors.color,\n textDecoration: 'none',\n borderRadius: '4px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n fontWeight: 500,\n transition: 'color 0.15s ease, background-color 0.15s ease',\n whiteSpace: 'nowrap',\n ...style,\n }\n\n return (\n <a\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={`demokit-powered-by ${className}`.trim()}\n style={baseStyles}\n onMouseOver={(e) => {\n e.currentTarget.style.color = colors.hoverColor\n e.currentTarget.style.backgroundColor = colors.hoverBackgroundColor\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.color = colors.color\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n >\n <span>Powered by DemoKit</span>\n <ExternalLinkIcon size={config.iconSize} />\n </a>\n )\n}\n","'use client'\n\nimport { useDemoMode } from './hooks'\nimport { PoweredByBadge } from './powered-by'\nimport type { DemoModeBannerProps } from './types'\n\n/**\n * Eye icon SVG component\n */\nfunction EyeIcon() {\n return (\n <svg\n width=\"20\"\n height=\"20\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <path d=\"M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z\" />\n <circle cx=\"12\" cy=\"12\" r=\"3\" />\n </svg>\n )\n}\n\n/**\n * Default styles for the banner\n */\nconst defaultStyles = {\n container: {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '8px 16px',\n backgroundColor: '#fef3c7',\n borderBottom: '1px solid rgba(217, 119, 6, 0.2)',\n fontSize: '14px',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n } as React.CSSProperties,\n content: {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n } as React.CSSProperties,\n icon: {\n color: '#d97706',\n flexShrink: 0,\n } as React.CSSProperties,\n label: {\n fontWeight: 600,\n color: '#78350f',\n } as React.CSSProperties,\n description: {\n color: 'rgba(120, 53, 15, 0.7)',\n fontSize: '12px',\n } as React.CSSProperties,\n button: {\n padding: '4px 12px',\n fontSize: '14px',\n backgroundColor: 'transparent',\n border: '1px solid rgba(217, 119, 6, 0.3)',\n borderRadius: '4px',\n cursor: 'pointer',\n color: '#78350f',\n fontFamily: 'inherit',\n transition: 'background-color 0.15s ease',\n } as React.CSSProperties,\n}\n\n/**\n * A ready-to-use banner component that shows when demo mode is active\n *\n * Displays a prominent amber banner with a label, description, and exit button.\n * Automatically hides when demo mode is disabled or before hydration.\n *\n * @example\n * function App() {\n * return (\n * <DemoKitProvider fixtures={fixtures}>\n * <DemoModeBanner />\n * <YourApp />\n * </DemoKitProvider>\n * )\n * }\n *\n * @example Custom labels\n * <DemoModeBanner\n * demoLabel=\"Preview Mode\"\n * description=\"You're viewing sample data\"\n * exitLabel=\"Exit Preview\"\n * />\n */\nexport function DemoModeBanner({\n className = '',\n exitLabel = 'Exit Demo Mode',\n demoLabel = 'Demo Mode Active',\n description = 'Changes are simulated and not saved',\n showIcon = true,\n showPoweredBy = true,\n poweredByUrl = 'https://demokit.ai',\n style,\n onExit,\n}: DemoModeBannerProps) {\n const { isDemoMode, isHydrated, disable } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated || !isDemoMode) {\n return null\n }\n\n const handleExit = () => {\n if (onExit) {\n onExit()\n } else {\n disable()\n }\n }\n\n // For OSS users, branding is always shown (enforced server-side in cloud)\n // The prop is only respected for paid cloud users\n const effectiveShowPoweredBy = showPoweredBy\n\n return (\n <div\n className={`demokit-banner ${className}`.trim()}\n style={{ ...defaultStyles.container, flexDirection: 'column', gap: '4px', ...style }}\n role=\"status\"\n aria-live=\"polite\"\n >\n <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%' }}>\n <div style={defaultStyles.content}>\n {showIcon && (\n <span style={defaultStyles.icon}>\n <EyeIcon />\n </span>\n )}\n <span style={defaultStyles.label}>{demoLabel}</span>\n {description && <span style={defaultStyles.description}>{description}</span>}\n </div>\n <button\n onClick={handleExit}\n style={defaultStyles.button}\n onMouseOver={(e) => {\n e.currentTarget.style.backgroundColor = 'rgba(217, 119, 6, 0.1)'\n }}\n onMouseOut={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n type=\"button\"\n >\n {exitLabel}\n </button>\n </div>\n {effectiveShowPoweredBy && (\n <div style={{ display: 'flex', justifyContent: 'flex-end', width: '100%' }}>\n <PoweredByBadge url={poweredByUrl} size=\"xs\" />\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport type { CSSProperties } from 'react'\nimport { useDemoMode } from './hooks'\nimport { PoweredByBadge } from './powered-by'\n\n/**\n * Props for the DemoModeToggle component\n */\nexport interface DemoModeToggleProps {\n /**\n * Show \"Demo Mode\" label next to the toggle\n * @default true\n */\n showLabel?: boolean\n\n /**\n * Label text to display\n * @default 'Demo Mode'\n */\n label?: string\n\n /**\n * Show \"Powered by DemoKit\" branding\n * Note: For OSS users, this is always true regardless of the prop value.\n * Only paid DemoKit Cloud users can hide the branding.\n * @default true\n */\n showPoweredBy?: boolean\n\n /**\n * URL for the \"Powered by\" link\n * @default 'https://demokit.ai'\n */\n poweredByUrl?: string\n\n /**\n * Position of the toggle\n * - 'inline': Renders where placed in the component tree\n * - 'floating': Fixed position, can be moved around\n * - 'corner': Fixed to bottom-right corner\n * @default 'inline'\n */\n position?: 'inline' | 'floating' | 'corner'\n\n /**\n * Size of the toggle\n * @default 'md'\n */\n size?: 'sm' | 'md' | 'lg'\n\n /**\n * Additional CSS class name\n */\n className?: string\n\n /**\n * Custom styles\n */\n style?: CSSProperties\n\n /**\n * Callback when toggle state changes\n */\n onChange?: (enabled: boolean) => void\n}\n\n/**\n * Size configurations for the toggle\n */\nconst sizeConfig = {\n sm: {\n trackWidth: 36,\n trackHeight: 20,\n thumbSize: 16,\n thumbOffset: 2,\n fontSize: '12px',\n padding: '8px 12px',\n gap: '8px',\n },\n md: {\n trackWidth: 44,\n trackHeight: 24,\n thumbSize: 20,\n thumbOffset: 2,\n fontSize: '14px',\n padding: '12px 16px',\n gap: '10px',\n },\n lg: {\n trackWidth: 52,\n trackHeight: 28,\n thumbSize: 24,\n thumbOffset: 2,\n fontSize: '16px',\n padding: '16px 20px',\n gap: '12px',\n },\n}\n\n/**\n * Position configurations\n */\nconst positionStyles: Record<string, CSSProperties> = {\n inline: {},\n floating: {\n position: 'fixed',\n zIndex: 9999,\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n },\n corner: {\n position: 'fixed',\n bottom: '20px',\n right: '20px',\n zIndex: 9999,\n boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',\n borderRadius: '8px',\n },\n}\n\n/**\n * Default styles\n */\nconst defaultStyles = {\n container: {\n display: 'inline-flex',\n flexDirection: 'column' as const,\n alignItems: 'flex-start',\n gap: '4px',\n backgroundColor: '#ffffff',\n border: '1px solid rgba(0, 0, 0, 0.1)',\n fontFamily:\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif',\n },\n row: {\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n },\n label: {\n fontWeight: 500,\n color: '#1f2937',\n userSelect: 'none' as const,\n },\n track: {\n position: 'relative' as const,\n borderRadius: '9999px',\n cursor: 'pointer',\n transition: 'background-color 0.2s ease',\n flexShrink: 0,\n },\n trackOff: {\n backgroundColor: '#d1d5db',\n },\n trackOn: {\n backgroundColor: '#d97706',\n },\n thumb: {\n position: 'absolute' as const,\n top: '50%',\n transform: 'translateY(-50%)',\n backgroundColor: '#ffffff',\n borderRadius: '50%',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n transition: 'left 0.2s ease',\n },\n poweredByContainer: {\n display: 'flex',\n justifyContent: 'flex-end',\n width: '100%',\n },\n}\n\n/**\n * A toggle switch component for enabling/disabling demo mode\n *\n * Supports inline placement or fixed positioning (floating/corner).\n * Includes optional \"Powered by DemoKit\" branding that is always shown for OSS users.\n *\n * @example Basic inline usage\n * <DemoModeToggle />\n *\n * @example Corner position (floating)\n * <DemoModeToggle position=\"corner\" />\n *\n * @example Without label\n * <DemoModeToggle showLabel={false} />\n *\n * @example With custom label and callback\n * <DemoModeToggle\n * label=\"Preview Mode\"\n * onChange={(enabled) => console.log('Demo mode:', enabled)}\n * />\n */\nexport function DemoModeToggle({\n showLabel = true,\n label = 'Demo Mode',\n showPoweredBy = true,\n poweredByUrl = 'https://demokit.ai',\n position = 'inline',\n size = 'md',\n className = '',\n style,\n onChange,\n}: DemoModeToggleProps) {\n const { isDemoMode, isHydrated, toggle } = useDemoMode()\n\n // Don't render until hydrated to avoid hydration mismatch\n if (!isHydrated) {\n return null\n }\n\n const config = sizeConfig[size]\n const posStyle = positionStyles[position]\n\n const handleToggle = () => {\n toggle()\n onChange?.(!isDemoMode)\n }\n\n const thumbLeft = isDemoMode\n ? config.trackWidth - config.thumbSize - config.thumbOffset\n : config.thumbOffset\n\n // For OSS users, branding is always shown (enforced server-side in cloud)\n // The prop is only respected for paid cloud users\n const effectiveShowPoweredBy = showPoweredBy\n\n return (\n <div\n className={`demokit-toggle ${className}`.trim()}\n style={{\n ...defaultStyles.container,\n padding: config.padding,\n ...posStyle,\n ...style,\n }}\n role=\"group\"\n aria-label=\"Demo mode toggle\"\n >\n <div style={{ ...defaultStyles.row, gap: config.gap }}>\n {showLabel && (\n <span style={{ ...defaultStyles.label, fontSize: config.fontSize }}>{label}</span>\n )}\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isDemoMode}\n aria-label={`${label}: ${isDemoMode ? 'On' : 'Off'}`}\n onClick={handleToggle}\n style={{\n ...defaultStyles.track,\n ...(isDemoMode ? defaultStyles.trackOn : defaultStyles.trackOff),\n width: config.trackWidth,\n height: config.trackHeight,\n border: 'none',\n padding: 0,\n }}\n >\n <span\n style={{\n ...defaultStyles.thumb,\n width: config.thumbSize,\n height: config.thumbSize,\n left: thumbLeft,\n }}\n />\n </button>\n </div>\n {effectiveShowPoweredBy && (\n <div style={defaultStyles.poweredByContainer}>\n <PoweredByBadge url={poweredByUrl} size=\"xs\" />\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime","sizeConfig","defaultStyles"]}
|