page.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. 'use client';
  2. import { useState } from 'react';
  3. import ImageInput from '../../components/ui/image-input';
  4. import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../components/ui/card';
  5. import { Button } from '../../components/ui/button';
  6. import { Label } from '../../components/ui/label';
  7. import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../components/ui/tabs';
  8. import { ImageValidationResult } from '../../lib/image-validation';
  9. export default function DemoPage() {
  10. const [input1, setInput1] = useState<File | string | null>(null);
  11. const [input2, setInput2] = useState<File | string | null>(null);
  12. const [validation1, setValidation1] = useState<ImageValidationResult | null>(null);
  13. const [validation2, setValidation2] = useState<ImageValidationResult | null>(null);
  14. const handleValidation1 = (result: ImageValidationResult) => {
  15. setValidation1(result);
  16. console.log('Input 1 validation:', result);
  17. };
  18. const handleValidation2 = (result: ImageValidationResult) => {
  19. setValidation2(result);
  20. console.log('Input 2 validation:', result);
  21. };
  22. const clearAll = () => {
  23. setInput1(null);
  24. setInput2(null);
  25. setValidation1(null);
  26. setValidation2(null);
  27. };
  28. return (
  29. <div className="min-h-screen bg-gray-50 p-8">
  30. <div className="max-w-4xl mx-auto space-y-8">
  31. <div className="text-center space-y-2">
  32. <h1 className="text-3xl font-bold text-gray-900">ImageInput Component Demo</h1>
  33. <p className="text-gray-600">Demonstration of the ImageInput component with both file upload and URL input modes</p>
  34. </div>
  35. <Tabs defaultValue="demo" className="w-full">
  36. <TabsList className="grid w-full grid-cols-3">
  37. <TabsTrigger value="demo">Demo</TabsTrigger>
  38. <TabsTrigger value="api">API Reference</TabsTrigger>
  39. <TabsTrigger value="examples">Examples</TabsTrigger>
  40. </TabsList>
  41. <TabsContent value="demo" className="space-y-6">
  42. {/* First ImageInput - Default Configuration */}
  43. <Card>
  44. <CardHeader>
  45. <CardTitle>Image Input 1 - Default Configuration</CardTitle>
  46. <CardDescription>
  47. File upload and URL input with default 10MB size limit
  48. </CardDescription>
  49. </CardHeader>
  50. <CardContent className="space-y-4">
  51. <ImageInput
  52. value={input1}
  53. onChange={setInput1}
  54. onValidation={handleValidation1}
  55. />
  56. {validation1 && (
  57. <div className="mt-4 p-4 bg-gray-100 rounded-lg">
  58. <Label className="font-semibold">Validation Result:</Label>
  59. <pre className="text-sm mt-2 overflow-auto">
  60. {JSON.stringify(validation1, null, 2)}
  61. </pre>
  62. </div>
  63. )}
  64. </CardContent>
  65. </Card>
  66. {/* Second ImageInput - Custom Configuration */}
  67. <Card>
  68. <CardHeader>
  69. <CardTitle>Image Input 2 - Custom Configuration</CardTitle>
  70. <CardDescription>
  71. Custom size limit (2MB) and without preview
  72. </CardDescription>
  73. </CardHeader>
  74. <CardContent className="space-y-4">
  75. <ImageInput
  76. value={input2}
  77. onChange={setInput2}
  78. onValidation={handleValidation2}
  79. maxSize={2 * 1024 * 1024} // 2MB
  80. showPreview={false}
  81. accept="image/jpeg,image/png,image/gif"
  82. placeholder="Enter a valid image URL or select a JPEG/PNG/GIF file"
  83. />
  84. {validation2 && (
  85. <div className="mt-4 p-4 bg-gray-100 rounded-lg">
  86. <Label className="font-semibold">Validation Result:</Label>
  87. <pre className="text-sm mt-2 overflow-auto">
  88. {JSON.stringify(validation2, null, 2)}
  89. </pre>
  90. </div>
  91. )}
  92. </CardContent>
  93. </Card>
  94. {/* Control Panel */}
  95. <Card>
  96. <CardHeader>
  97. <CardTitle>Current Values</CardTitle>
  98. <CardDescription>
  99. Shows the current state of both inputs
  100. </CardDescription>
  101. </CardHeader>
  102. <CardContent className="space-y-4">
  103. <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
  104. <div>
  105. <Label>Input 1 Value:</Label>
  106. <div className="mt-1 p-2 bg-gray-100 rounded text-sm">
  107. {input1 instanceof File
  108. ? `File: ${input1.name} (${(input1.size / 1024 / 1024).toFixed(2)} MB)`
  109. : input1 || 'null'
  110. }
  111. </div>
  112. </div>
  113. <div>
  114. <Label>Input 2 Value:</Label>
  115. <div className="mt-1 p-2 bg-gray-100 rounded text-sm">
  116. {input2 instanceof File
  117. ? `File: ${input2.name} (${(input2.size / 1024 / 1024).toFixed(2)} MB)`
  118. : input2 || 'null'
  119. }
  120. </div>
  121. </div>
  122. </div>
  123. <Button onClick={clearAll} variant="outline" className="w-full">
  124. Clear All Inputs
  125. </Button>
  126. </CardContent>
  127. </Card>
  128. </TabsContent>
  129. <TabsContent value="api" className="space-y-6">
  130. <Card>
  131. <CardHeader>
  132. <CardTitle>Props</CardTitle>
  133. <CardDescription>
  134. Available props for the ImageInput component
  135. </CardDescription>
  136. </CardHeader>
  137. <CardContent>
  138. <div className="overflow-x-auto">
  139. <table className="w-full text-sm">
  140. <thead>
  141. <tr className="border-b">
  142. <th className="text-left p-2">Prop</th>
  143. <th className="text-left p-2">Type</th>
  144. <th className="text-left p-2">Default</th>
  145. <th className="text-left p-2">Description</th>
  146. </tr>
  147. </thead>
  148. <tbody className="space-y-2">
  149. <tr className="border-b">
  150. <td className="p-2 font-mono">value</td>
  151. <td className="p-2 font-mono">File | string | null</td>
  152. <td className="p-2 font-mono">undefined</td>
  153. <td className="p-2">Current value of the input</td>
  154. </tr>
  155. <tr className="border-b">
  156. <td className="p-2 font-mono">onChange</td>
  157. <td className="p-2 font-mono">(File | string | null) =&gt; void</td>
  158. <td className="p-2 font-mono">-</td>
  159. <td className="p-2">Callback when value changes</td>
  160. </tr>
  161. <tr className="border-b">
  162. <td className="p-2 font-mono">onValidation</td>
  163. <td className="p-2 font-mono">(ImageValidationResult) =&gt; void</td>
  164. <td className="p-2 font-mono">-</td>
  165. <td className="p-2">Callback for validation results</td>
  166. </tr>
  167. <tr className="border-b">
  168. <td className="p-2 font-mono">disabled</td>
  169. <td className="p-2 font-mono">boolean</td>
  170. <td className="p-2 font-mono">false</td>
  171. <td className="p-2">Disable the input</td>
  172. </tr>
  173. <tr className="border-b">
  174. <td className="p-2 font-mono">maxSize</td>
  175. <td className="p-2 font-mono">number</td>
  176. <td className="p-2 font-mono">10MB</td>
  177. <td className="p-2">Maximum file size in bytes</td>
  178. </tr>
  179. <tr className="border-b">
  180. <td className="p-2 font-mono">accept</td>
  181. <td className="p-2 font-mono">string</td>
  182. <td className="p-2 font-mono">image/*</td>
  183. <td className="p-2">Accepted file types</td>
  184. </tr>
  185. <tr className="border-b">
  186. <td className="p-2 font-mono">placeholder</td>
  187. <td className="p-2 font-mono">string</td>
  188. <td className="p-2 font-mono">-</td>
  189. <td className="p-2">Placeholder for URL input</td>
  190. </tr>
  191. <tr className="border-b">
  192. <td className="p-2 font-mono">showPreview</td>
  193. <td className="p-2 font-mono">boolean</td>
  194. <td className="p-2 font-mono">true</td>
  195. <td className="p-2">Show image preview</td>
  196. </tr>
  197. <tr>
  198. <td className="p-2 font-mono">className</td>
  199. <td className="p-2 font-mono">string</td>
  200. <td className="p-2 font-mono">-</td>
  201. <td className="p-2">Additional CSS classes</td>
  202. </tr>
  203. </tbody>
  204. </table>
  205. </div>
  206. </CardContent>
  207. </Card>
  208. </TabsContent>
  209. <TabsContent value="examples" className="space-y-6">
  210. <Card>
  211. <CardHeader>
  212. <CardTitle>Usage Examples</CardTitle>
  213. <CardDescription>
  214. Common patterns for using the ImageInput component
  215. </CardDescription>
  216. </CardHeader>
  217. <CardContent className="space-y-6">
  218. <div>
  219. <Label className="text-base font-semibold">Basic Usage</Label>
  220. <pre className="mt-2 p-4 bg-gray-100 rounded-lg text-sm overflow-x-auto">
  221. {`import ImageInput from './components/ui/image-input';
  222. const [image, setImage] = useState<File | string | null>(null);
  223. <ImageInput
  224. value={image}
  225. onChange={setImage}
  226. />`}
  227. </pre>
  228. </div>
  229. <div>
  230. <Label className="text-base font-semibold">With Validation Callback</Label>
  231. <pre className="mt-2 p-4 bg-gray-100 rounded-lg text-sm overflow-x-auto">
  232. {`const [image, setImage] = useState<File | string | null>(null);
  233. const [validation, setValidation] = useState<ImageValidationResult | null>(null);
  234. const handleValidation = (result: ImageValidationResult) => {
  235. setValidation(result);
  236. console.log('Is valid:', result.isValid);
  237. console.log('File name:', result.filename);
  238. };
  239. <ImageInput
  240. value={image}
  241. onChange={setImage}
  242. onValidation={handleValidation}
  243. />`}
  244. </pre>
  245. </div>
  246. <div>
  247. <Label className="text-base font-semibold">Custom Configuration</Label>
  248. <pre className="mt-2 p-4 bg-gray-100 rounded-lg text-sm overflow-x-auto">
  249. {`<ImageInput
  250. value={image}
  251. onChange={setImage}
  252. maxSize={5 * 1024 * 1024} // 5MB
  253. accept="image/jpeg,image/png"
  254. showPreview={true}
  255. placeholder="Enter image URL or select JPEG/PNG file"
  256. className="custom-image-input"
  257. />`}
  258. </pre>
  259. </div>
  260. <div>
  261. <Label className="text-base font-semibold">Disabled State</Label>
  262. <pre className="mt-2 p-4 bg-gray-100 rounded-lg text-sm overflow-x-auto">
  263. {`const [isUploading, setIsUploading] = useState(false);
  264. <ImageInput
  265. value={image}
  266. onChange={setImage}
  267. disabled={isUploading}
  268. />`}
  269. </pre>
  270. </div>
  271. </CardContent>
  272. </Card>
  273. </TabsContent>
  274. </Tabs>
  275. </div>
  276. </div>
  277. );
  278. }