@djangocfg/ui-nextjs 2.1.4 → 2.1.6

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.
@@ -1,10 +1,10 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useCallback } from 'react';
4
- import { Button, Card, CardContent, CardHeader, CardTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, Badge, Skeleton } from '@djangocfg/ui-core/components';
5
- import { Play, Copy, RotateCcw, Loader2, Key, Settings, Send } from 'lucide-react';
4
+ import { Button, Card, CardContent, CardHeader, CardTitle, Input, Textarea, CopyButton } from '@djangocfg/ui-core/components';
5
+ import { Loader2, Key, Send } from 'lucide-react';
6
6
  import { usePlaygroundContext } from '../context/PlaygroundContext';
7
- import { findApiKeyById, logApiKeyUsage } from '../utils';
7
+ import { findApiKeyById } from '../utils';
8
8
  import { isValidJson, parseRequestHeaders } from '../utils';
9
9
  import { RequestParametersForm } from './RequestParametersForm';
10
10
  import { EndpointInfo } from './EndpointInfo';
@@ -14,16 +14,8 @@ export const RequestBuilder: React.FC = () => {
14
14
  const {
15
15
  state,
16
16
  apiKeys,
17
- apiKeysLoading,
18
- setRequestUrl,
19
- setRequestMethod,
20
- setRequestHeaders,
21
17
  setRequestBody,
22
- setResponse,
23
- setLoading,
24
- setSelectedApiKey,
25
18
  setManualApiToken,
26
- copyToClipboard,
27
19
  sendRequest
28
20
  } = usePlaygroundContext();
29
21
 
@@ -143,15 +135,13 @@ export const RequestBuilder: React.FC = () => {
143
135
  <CardHeader>
144
136
  <div className="flex items-center justify-between">
145
137
  <CardTitle className="text-sm text-foreground">cURL Command</CardTitle>
146
- <Button
138
+ <CopyButton
139
+ value={curlCommand}
147
140
  variant="outline"
148
141
  size="sm"
149
- onClick={() => copyToClipboard(curlCommand)}
150
- disabled={!curlCommand}
151
142
  >
152
- <Copy className="h-4 w-4" />
153
- <span className="hidden sm:inline">Copy cURL</span>
154
- </Button>
143
+ Copy cURL
144
+ </CopyButton>
155
145
  </div>
156
146
  </CardHeader>
157
147
  <CardContent>
@@ -1,16 +1,35 @@
1
1
  'use client';
2
2
 
3
- import React from 'react';
4
- import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@djangocfg/ui-core/components';
5
- import { Copy, XCircle, Download } from 'lucide-react';
3
+ import React, { useMemo, useCallback } from 'react';
4
+ import { Badge, Button, Card, CardContent, CardHeader, CardTitle, CopyButton } from '@djangocfg/ui-core/components';
5
+ import { XCircle, Download } from 'lucide-react';
6
6
  import { usePlaygroundContext } from '../context/PlaygroundContext';
7
7
  import { getStatusColor } from '../utils';
8
8
  import JsonTree from '../../JsonTree';
9
9
 
10
10
  export const ResponseViewer: React.FC = () => {
11
- const { state, copyToClipboard } = usePlaygroundContext();
11
+ const { state } = usePlaygroundContext();
12
12
  const { response } = state;
13
13
 
14
+ // Memoize response text for copy/download
15
+ const responseText = useMemo(() => {
16
+ if (!response?.data) return '';
17
+ return typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
18
+ }, [response?.data]);
19
+
20
+ const handleDownloadResponse = useCallback(() => {
21
+ if (!responseText) return;
22
+ const blob = new Blob([responseText], { type: 'application/json' });
23
+ const url = URL.createObjectURL(blob);
24
+ const a = document.createElement('a');
25
+ a.href = url;
26
+ a.download = 'response.json';
27
+ document.body.appendChild(a);
28
+ a.click();
29
+ document.body.removeChild(a);
30
+ URL.revokeObjectURL(url);
31
+ }, [responseText]);
32
+
14
33
  if (!response) {
15
34
  return (
16
35
  <div className="space-y-4">
@@ -26,33 +45,14 @@ export const ResponseViewer: React.FC = () => {
26
45
  );
27
46
  }
28
47
 
29
- const handleCopyResponse = () => {
30
- const responseText = typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
31
- copyToClipboard(responseText);
32
- };
33
-
34
- const handleDownloadResponse = () => {
35
- const responseText = typeof response.data === 'string' ? response.data : JSON.stringify(response.data, null, 2);
36
- const blob = new Blob([responseText], { type: 'application/json' });
37
- const url = URL.createObjectURL(blob);
38
- const a = document.createElement('a');
39
- a.href = url;
40
- a.download = 'response.json';
41
- document.body.appendChild(a);
42
- a.click();
43
- document.body.removeChild(a);
44
- URL.revokeObjectURL(url);
45
- };
46
-
47
48
  return (
48
49
  <div className="space-y-4">
49
50
  <div className="flex items-center justify-between">
50
51
  <h2 className="text-lg font-semibold text-foreground">Response</h2>
51
52
  <div className="flex items-center space-x-2">
52
- <Button variant="outline" size="sm" onClick={handleCopyResponse}>
53
- <Copy className="h-4 w-4" />
54
- <span className="hidden sm:inline">Copy</span>
55
- </Button>
53
+ <CopyButton value={responseText} variant="outline" size="sm">
54
+ Copy
55
+ </CopyButton>
56
56
  <Button variant="outline" size="sm" onClick={handleDownloadResponse}>
57
57
  <Download className="h-4 w-4" />
58
58
  <span className="hidden sm:inline">Download</span>
@@ -5,7 +5,6 @@ import consola from 'consola';
5
5
  import { type ApiEndpoint, type ApiResponse, type PlaygroundContextType, type PlaygroundState, type PlaygroundStep, type PlaygroundConfig } from '../types';
6
6
  import { getDefaultVersion } from '../utils/versionManager';
7
7
  import { parseRequestHeaders, substituteUrlParameters } from '../utils';
8
- import { useCopy } from '@djangocfg/ui-core/hooks';
9
8
 
10
9
  const createInitialState = (): PlaygroundState => ({
11
10
  // Step management
@@ -59,11 +58,6 @@ export const PlaygroundProvider: React.FC<PlaygroundProviderProps> = ({ children
59
58
  const apiKeys = React.useMemo(() => [], []);
60
59
  const isLoadingApiKeys = false;
61
60
 
62
- const { copyToClipboard } = useCopy({
63
- successMessage: "cURL command copied to clipboard",
64
- errorMessage: "Failed to copy cURL command"
65
- });
66
-
67
61
  const updateState = (updates: Partial<PlaygroundState>) => {
68
62
  setState((prev) => ({ ...prev, ...updates }));
69
63
  };
@@ -330,7 +324,6 @@ export const PlaygroundProvider: React.FC<PlaygroundProviderProps> = ({ children
330
324
 
331
325
  // Actions
332
326
  clearAll,
333
- copyToClipboard,
334
327
  sendRequest,
335
328
  };
336
329
 
@@ -135,7 +135,6 @@ export interface PlaygroundContextType {
135
135
 
136
136
  // Actions
137
137
  clearAll: () => void;
138
- copyToClipboard: (text: string) => void;
139
138
  sendRequest: () => Promise<void>;
140
139
  }
141
140
 
@@ -1,10 +1,9 @@
1
1
  'use client';
2
2
 
3
3
  import { Highlight, Language, themes } from 'prism-react-renderer';
4
- import React, { useState } from 'react';
5
- import { Copy, Check } from 'lucide-react';
6
- import { useCopy } from '@djangocfg/ui-core/hooks';
7
- import { useTheme } from '../../hooks/useTheme';
4
+ import React from 'react';
5
+ import { CopyButton } from '@djangocfg/ui-core/components';
6
+ import { useResolvedTheme } from '../../hooks/useResolvedTheme';
8
7
 
9
8
  interface PrettyCodeProps {
10
9
  data: string | object;
@@ -16,12 +15,7 @@ interface PrettyCodeProps {
16
15
  }
17
16
 
18
17
  const PrettyCode = ({ data, language, className, mode, inline = false, customBg }: PrettyCodeProps) => {
19
- const detectedTheme = useTheme();
20
- const [copied, setCopied] = useState(false);
21
- const { copyToClipboard } = useCopy({
22
- successMessage: "Code copied to clipboard",
23
- errorMessage: "Failed to copy code"
24
- });
18
+ const detectedTheme = useResolvedTheme();
25
19
 
26
20
  // Use provided mode or fall back to detected theme
27
21
  const currentTheme = mode || detectedTheme;
@@ -32,15 +26,6 @@ const PrettyCode = ({ data, language, className, mode, inline = false, customBg
32
26
 
33
27
  // Convert form object to JSON string with proper formatting
34
28
  const contentJson = typeof data === 'string' ? data : JSON.stringify(data || {}, null, 2);
35
-
36
- // Handle copy
37
- const handleCopy = async () => {
38
- const success = await copyToClipboard(contentJson);
39
- if (success) {
40
- setCopied(true);
41
- setTimeout(() => setCopied(false), 2000);
42
- }
43
- };
44
29
 
45
30
  // Handle empty content
46
31
  if (!contentJson || contentJson.trim() === '') {
@@ -173,13 +158,13 @@ const PrettyCode = ({ data, language, className, mode, inline = false, customBg
173
158
  <span className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-background/80 text-muted-foreground border border-border/50 backdrop-blur-sm">
174
159
  {displayLanguage}
175
160
  </span>
176
- <button
177
- onClick={handleCopy}
178
- className="inline-flex items-center justify-center p-1.5 rounded text-muted-foreground hover:text-foreground bg-background/80 border border-border/50 backdrop-blur-sm transition-colors"
161
+ <CopyButton
162
+ value={contentJson}
163
+ variant="ghost"
164
+ className="h-7 w-7 bg-background/80 border border-border/50 backdrop-blur-sm"
165
+ iconClassName="h-3.5 w-3.5"
179
166
  title="Copy code"
180
- >
181
- {copied ? <Check className="h-3.5 w-3.5 text-green-500" /> : <Copy className="h-3.5 w-3.5" />}
182
- </button>
167
+ />
183
168
  </div>
184
169
 
185
170
  <div className="h-full overflow-auto">
@@ -194,20 +194,20 @@ export const VideoPlayer = forwardRef<VideoPlayerRef, VideoPlayerProps>(({
194
194
  <MediaProvider />
195
195
 
196
196
  {/* Poster with proper aspect ratio handling */}
197
- {source.poster && (
197
+ {posterUrl && (
198
198
  <Poster
199
199
  className="vds-poster"
200
- src={source.poster}
200
+ src={posterUrl}
201
201
  alt={source.title || 'Video poster'}
202
202
  style={{ objectFit: 'cover' }}
203
203
  />
204
204
  )}
205
-
205
+
206
206
  {/* Use Vidstack's built-in default layout */}
207
207
  {controls && (
208
208
  <DefaultVideoLayout
209
209
  icons={defaultLayoutIcons}
210
- thumbnails={source.poster}
210
+ thumbnails={posterUrl}
211
211
  />
212
212
  )}
213
213
  </MediaPlayer>