@pichaflow/react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 James Sinkala (PichaFlow)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { UploadResponse } from '@pichaflow/sdk';
3
+
4
+ interface PichaFlowUploadProps {
5
+ apiKey: string;
6
+ baseUrl?: string;
7
+ uploadUrl?: string;
8
+ fetchUrl?: string;
9
+ useSecure?: boolean;
10
+ tags?: string[];
11
+ tenantId?: string;
12
+ onSuccess?: (response: UploadResponse) => void;
13
+ onSuccessAll?: (responses: UploadResponse[]) => void;
14
+ onError?: (error: any) => void;
15
+ onProgress?: (progress: number) => void;
16
+ allowDeletion?: boolean;
17
+ onDelete?: (id: string) => void;
18
+ onDeleteError?: (error: any) => void;
19
+ className?: string;
20
+ icon?: React.ReactNode;
21
+ }
22
+ declare const PichaFlowUpload: React.FC<PichaFlowUploadProps>;
23
+
24
+ export { PichaFlowUpload };
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { UploadResponse } from '@pichaflow/sdk';
3
+
4
+ interface PichaFlowUploadProps {
5
+ apiKey: string;
6
+ baseUrl?: string;
7
+ uploadUrl?: string;
8
+ fetchUrl?: string;
9
+ useSecure?: boolean;
10
+ tags?: string[];
11
+ tenantId?: string;
12
+ onSuccess?: (response: UploadResponse) => void;
13
+ onSuccessAll?: (responses: UploadResponse[]) => void;
14
+ onError?: (error: any) => void;
15
+ onProgress?: (progress: number) => void;
16
+ allowDeletion?: boolean;
17
+ onDelete?: (id: string) => void;
18
+ onDeleteError?: (error: any) => void;
19
+ className?: string;
20
+ icon?: React.ReactNode;
21
+ }
22
+ declare const PichaFlowUpload: React.FC<PichaFlowUploadProps>;
23
+
24
+ export { PichaFlowUpload };
package/dist/index.js ADDED
@@ -0,0 +1,96 @@
1
+ "use strict";var k=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var V=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var q=(l,i)=>{for(var g in i)k(l,g,{get:i[g],enumerable:!0})},G=(l,i,g,v)=>{if(i&&typeof i=="object"||typeof i=="function")for(let c of V(i))!K.call(l,c)&&c!==g&&k(l,c,{get:()=>i[c],enumerable:!(v=O(i,c))||v.enumerable});return l};var J=l=>G(k({},"__esModule",{value:!0}),l);var Q={};q(Q,{PichaFlowUpload:()=>R});module.exports=J(Q);var b=require("react"),y=require("@pichaflow/sdk"),a=require("react/jsx-runtime"),R=({apiKey:l,baseUrl:i,useSecure:g,tags:v,onSuccess:c,onSuccessAll:P,onError:U,onProgress:z,allowDeletion:T,onDelete:C,onDeleteError:I,className:L,tenantId:E,uploadUrl:$,fetchUrl:S,icon:A})=>{let[M,x]=(0,b.useState)(!1),[N,m]=(0,b.useState)([]),j=(0,b.useRef)(null),w=new y.PichaFlowClient({apiKey:l,baseUrl:i,uploadUrl:$,fetchUrl:S,tenantId:E}),D=async e=>{if(e.length===0)return;let t=e.filter(s=>s.type.startsWith("image/"));if(t.length===0){U?.(new Error("Only image files are supported."));return}let r=t.map(s=>({id:crypto.randomUUID(),file:s,progress:0,status:"pending"}));m(s=>[...s,...r]);let o=0,n=[];await Promise.all(r.map(async s=>{let u=p=>{m(f=>{let d=f.findIndex(B=>B.id===s.id);if(d===-1)return f;let h=[...f];return h[d]={...h[d],...p},h})};try{let p=await(0,y.optimizeImageForUpload)(s.file);u({status:"uploading",file:p});let f={tags:v,onProgress:h=>{u({progress:h}),z?.(h)}},d=g?await w.secureUpload(p,f):await w.upload(p,f);u({status:"success",progress:100,response:d}),n.push(d),c?.(d)}catch(p){u({status:"error",error:p.message||"Upload failed"}),U?.(p)}finally{o++,o===r.length&&n.length>0&&P?.(n)}}))},H=e=>{e.preventDefault(),x(!1);let t=Array.from(e.dataTransfer.files||[]);t.length&&D(t)},W=async(e,t)=>{m(r=>{let o=r.findIndex(s=>s.id===e);if(o===-1)return r;let n=[...r];return n[o]={...n[o],status:"deleting"},n});try{await w.deleteAsset(t),m(r=>r.filter(o=>o.id!==e)),C?.(t)}catch(r){m(o=>{let n=o.findIndex(u=>u.id===e);if(n===-1)return o;let s=[...o];return s[n]={...s[n],status:"success"},s}),I?.(r)}},F=e=>{let t=Array.from(e.target.files||[]);t.length&&D(t),e.target.value=""};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{children:`
2
+ .picha-upload-zone {
3
+ border: 2px dashed #e2e8f0;
4
+ border-radius: 0.75rem;
5
+ padding: 2rem;
6
+ transition: all 0.2s ease;
7
+ background: #f8fafc;
8
+ }
9
+ .picha-upload-zone.is-dragging {
10
+ border-color: #3b82f6;
11
+ background: #eff6ff;
12
+ }
13
+ .picha-upload-content {
14
+ text-align: center;
15
+ }
16
+ .picha-upload-icon { width: 2rem; height: 2rem; color: #94a3b8; margin: 0 auto 1rem; display: block; }
17
+ .picha-upload-text { color: #64748b; font-size: 0.875rem; margin: 0; }
18
+ .picha-upload-link { color: #3b82f6; font-weight: 600; cursor: pointer; }
19
+
20
+ .picha-uploading-list {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: 1rem;
24
+ }
25
+ .picha-task-item {
26
+ background: white;
27
+ padding: 1rem;
28
+ border-radius: 0.5rem;
29
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
30
+ border: 1px solid #f1f5f9;
31
+ }
32
+ .picha-task-info {
33
+ display: flex;
34
+ justify-content: space-between;
35
+ align-items: center;
36
+ margin-bottom: 0.5rem;
37
+ }
38
+ .picha-file-name {
39
+ font-size: 0.875rem;
40
+ font-weight: 500;
41
+ color: #334155;
42
+ white-space: nowrap;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ max-width: 70%;
46
+ }
47
+ .picha-status-text {
48
+ font-size: 0.75rem;
49
+ font-weight: 600;
50
+ color: #64748b;
51
+ }
52
+ .picha-status-text.success { color: #10b981; }
53
+ .picha-status-text.error { color: #ef4444; }
54
+ .picha-status-text.deleting { color: #f59e0b; }
55
+
56
+ .picha-delete-btn {
57
+ background: none;
58
+ border: none;
59
+ color: #94a3b8;
60
+ cursor: pointer;
61
+ padding: 0.25rem;
62
+ border-radius: 0.25rem;
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ transition: all 0.2s;
67
+ }
68
+ .picha-delete-btn:hover:not(:disabled) {
69
+ color: #ef4444;
70
+ background: #fef2f2;
71
+ }
72
+ .picha-delete-btn:disabled {
73
+ opacity: 0.5;
74
+ cursor: not-allowed;
75
+ }
76
+
77
+ .picha-progress-bar { background: #e2e8f0; height: 0.5rem; border-radius: 999px; overflow: hidden; }
78
+ .picha-progress-fill { background: #3b82f6; height: 100%; transition: width 0.1s ease; }
79
+ .picha-progress-fill.success { background: #10b981; }
80
+ .picha-progress-fill.error { background: #ef4444; }
81
+
82
+ .picha-error-text {
83
+ margin-top: 0.25rem;
84
+ font-size: 0.75rem;
85
+ color: #ef4444;
86
+ }
87
+ .picha-add-more { text-align: center; margin-top: 1.5rem; }
88
+
89
+ @media (prefers-color-scheme: dark) {
90
+ .picha-upload-zone { background: #0f172a; border-color: #1e293b; }
91
+ .picha-task-item { background: #1e293b; border-color: #334155; }
92
+ .picha-file-name { color: #f8fafc; }
93
+ .picha-progress-bar { background: #334155; }
94
+ }
95
+ `}),(0,a.jsx)("div",{onDragOver:e=>{e.preventDefault(),x(!0)},onDragLeave:e=>{e.preventDefault(),x(!1)},onDrop:H,className:`picha-upload-zone ${M?"is-dragging":""} ${L||""}`,children:N.length===0?(0,a.jsxs)("div",{className:"picha-upload-content",children:[A||(0,a.jsx)("svg",{className:"picha-upload-icon",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,a.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"})}),(0,a.jsxs)("p",{className:"picha-upload-text",children:["Drag & drop or ",(0,a.jsxs)("label",{className:"picha-upload-link",children:["browse",(0,a.jsx)("input",{type:"file",accept:"image/*",multiple:!0,ref:j,onChange:F,style:{display:"none"}})]})]})]}):(0,a.jsxs)("div",{className:"picha-uploading-list",children:[N.map(e=>(0,a.jsxs)("div",{className:"picha-task-item",children:[(0,a.jsxs)("div",{className:"picha-task-info",children:[(0,a.jsx)("span",{className:"picha-file-name",children:e.file.name}),(0,a.jsxs)("div",{className:"picha-task-actions",style:{display:"flex",alignItems:"center",gap:"0.5rem"},children:[(0,a.jsx)("span",{className:`picha-status-text ${e.status}`,children:e.status==="success"?"Uploaded":e.status==="error"?"Failed":e.status==="deleting"?"Deleting...":`${e.progress}%`}),T&&(e.status==="success"||e.status==="deleting")&&e.response?.id&&(0,a.jsx)("button",{onClick:()=>W(e.id,e.response.id),className:"picha-delete-btn",title:"Delete asset",disabled:e.status==="deleting",children:(0,a.jsxs)("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[(0,a.jsx)("polyline",{points:"3 6 5 6 21 6"}),(0,a.jsx)("path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"}),(0,a.jsx)("line",{x1:"10",y1:"11",x2:"10",y2:"17"}),(0,a.jsx)("line",{x1:"14",y1:"11",x2:"14",y2:"17"})]})})]})]}),(0,a.jsx)("div",{className:"picha-progress-bar",children:(0,a.jsx)("div",{className:`picha-progress-fill ${e.status}`,style:{width:`${e.progress}%`}})}),e.error&&(0,a.jsx)("p",{className:"picha-error-text",children:e.error})]},e.id)),(0,a.jsx)("div",{className:"picha-add-more",children:(0,a.jsx)("p",{className:"picha-upload-text",children:(0,a.jsxs)("label",{className:"picha-upload-link",children:["Upload more files",(0,a.jsx)("input",{type:"file",accept:"image/*",multiple:!0,onChange:F,style:{display:"none"}})]})})})]})})]})};0&&(module.exports={PichaFlowUpload});
96
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/PichaFlowUpload.tsx"],"sourcesContent":["export { PichaFlowUpload } from './PichaFlowUpload';\n","import React, { useState, useRef } from 'react';\nimport { PichaFlowClient, type UploadResponse, optimizeImageForUpload } from '@pichaflow/sdk';\n\nexport interface PichaFlowUploadProps {\n apiKey: string;\n baseUrl?: string;\n uploadUrl?: string;\n fetchUrl?: string;\n useSecure?: boolean;\n tags?: string[];\n tenantId?: string;\n onSuccess?: (response: UploadResponse) => void;\n onSuccessAll?: (responses: UploadResponse[]) => void;\n onError?: (error: any) => void;\n onProgress?: (progress: number) => void;\n allowDeletion?: boolean;\n onDelete?: (id: string) => void;\n onDeleteError?: (error: any) => void;\n className?: string;\n icon?: React.ReactNode;\n}\n\ntype UploadTask = {\n id: string;\n file: File;\n progress: number;\n status: 'pending' | 'uploading' | 'success' | 'error' | 'deleting' | 'deleted';\n error?: string;\n response?: UploadResponse;\n};\n\nexport const PichaFlowUpload: React.FC<PichaFlowUploadProps> = ({\n apiKey,\n baseUrl,\n useSecure,\n tags,\n onSuccess,\n onSuccessAll,\n onError,\n onProgress,\n allowDeletion,\n onDelete,\n onDeleteError,\n className,\n tenantId,\n uploadUrl,\n fetchUrl,\n icon\n}) => {\n const [isDragging, setIsDragging] = useState(false);\n const [tasks, setTasks] = useState<UploadTask[]>([]);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const client = new PichaFlowClient({ apiKey, baseUrl, uploadUrl, fetchUrl, tenantId });\n\n const handleFiles = async (files: File[]) => {\n if (files.length === 0) return;\n\n const imageFiles = files.filter(f => f.type.startsWith('image/'));\n if (imageFiles.length === 0) {\n onError?.(new Error('Only image files are supported.'));\n return;\n }\n\n const previewTasks = imageFiles.map(file => ({\n id: crypto.randomUUID(),\n file,\n progress: 0,\n status: 'pending' as const\n }));\n\n setTasks(prev => [...prev, ...previewTasks]);\n\n let completedCount = 0;\n const successfulResponses: UploadResponse[] = [];\n\n await Promise.all(previewTasks.map(async (task) => {\n const updateTask = (updates: Partial<UploadTask>) => {\n setTasks(currentTasks => {\n const index = currentTasks.findIndex(t => t.id === task.id);\n if (index === -1) return currentTasks;\n const newTasks = [...currentTasks];\n newTasks[index] = { ...newTasks[index], ...updates };\n return newTasks;\n });\n };\n\n try {\n const optimizedFile = await optimizeImageForUpload(task.file);\n updateTask({ status: 'uploading', file: optimizedFile });\n\n const options = {\n tags,\n onProgress: (p: number) => {\n updateTask({ progress: p });\n onProgress?.(p);\n }\n };\n\n const response = useSecure\n ? await client.secureUpload(optimizedFile, options)\n : await client.upload(optimizedFile, options);\n\n updateTask({ status: 'success', progress: 100, response });\n successfulResponses.push(response);\n onSuccess?.(response);\n } catch (err: any) {\n updateTask({ status: 'error', error: err.message || 'Upload failed' });\n onError?.(err);\n } finally {\n completedCount++;\n if (completedCount === previewTasks.length) {\n if (successfulResponses.length > 0) {\n onSuccessAll?.(successfulResponses);\n }\n }\n }\n }));\n };\n\n const onDrop = (e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(false);\n const files = Array.from(e.dataTransfer.files || []) as File[];\n if (files.length) handleFiles(files);\n };\n\n const handleDelete = async (taskId: string, assetId: string) => {\n setTasks(current => {\n const index = current.findIndex(t => t.id === taskId);\n if (index === -1) return current;\n const newTasks = [...current];\n newTasks[index] = { ...newTasks[index], status: 'deleting' };\n return newTasks;\n });\n\n try {\n await client.deleteAsset(assetId);\n setTasks(current => current.filter(t => t.id !== taskId));\n onDelete?.(assetId);\n } catch (err: any) {\n setTasks(current => {\n const index = current.findIndex(t => t.id === taskId);\n if (index === -1) return current;\n const newTasks = [...current];\n newTasks[index] = { ...newTasks[index], status: 'success' };\n return newTasks;\n });\n onDeleteError?.(err);\n }\n };\n\n const onFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(e.target.files || []) as File[];\n if (files.length) handleFiles(files);\n e.target.value = ''; // Reset\n };\n\n return (\n <>\n <style>{`\n .picha-upload-zone {\n border: 2px dashed #e2e8f0;\n border-radius: 0.75rem;\n padding: 2rem;\n transition: all 0.2s ease;\n background: #f8fafc;\n }\n .picha-upload-zone.is-dragging {\n border-color: #3b82f6;\n background: #eff6ff;\n }\n .picha-upload-content {\n text-align: center;\n }\n .picha-upload-icon { width: 2rem; height: 2rem; color: #94a3b8; margin: 0 auto 1rem; display: block; }\n .picha-upload-text { color: #64748b; font-size: 0.875rem; margin: 0; }\n .picha-upload-link { color: #3b82f6; font-weight: 600; cursor: pointer; }\n \n .picha-uploading-list {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n }\n .picha-task-item {\n background: white;\n padding: 1rem;\n border-radius: 0.5rem;\n box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n border: 1px solid #f1f5f9;\n }\n .picha-task-info {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.5rem;\n }\n .picha-file-name {\n font-size: 0.875rem;\n font-weight: 500;\n color: #334155;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 70%;\n }\n .picha-status-text {\n font-size: 0.75rem;\n font-weight: 600;\n color: #64748b;\n }\n .picha-status-text.success { color: #10b981; }\n .picha-status-text.error { color: #ef4444; }\n .picha-status-text.deleting { color: #f59e0b; }\n \n .picha-delete-btn {\n background: none;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 0.25rem;\n border-radius: 0.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n }\n .picha-delete-btn:hover:not(:disabled) {\n color: #ef4444;\n background: #fef2f2;\n }\n .picha-delete-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n \n .picha-progress-bar { background: #e2e8f0; height: 0.5rem; border-radius: 999px; overflow: hidden; }\n .picha-progress-fill { background: #3b82f6; height: 100%; transition: width 0.1s ease; }\n .picha-progress-fill.success { background: #10b981; }\n .picha-progress-fill.error { background: #ef4444; }\n \n .picha-error-text {\n margin-top: 0.25rem;\n font-size: 0.75rem;\n color: #ef4444;\n }\n .picha-add-more { text-align: center; margin-top: 1.5rem; }\n\n @media (prefers-color-scheme: dark) {\n .picha-upload-zone { background: #0f172a; border-color: #1e293b; }\n .picha-task-item { background: #1e293b; border-color: #334155; }\n .picha-file-name { color: #f8fafc; }\n .picha-progress-bar { background: #334155; }\n }\n `}</style>\n\n <div\n onDragOver={(e) => { e.preventDefault(); setIsDragging(true); }}\n onDragLeave={(e) => { e.preventDefault(); setIsDragging(false); }}\n onDrop={onDrop}\n className={`picha-upload-zone ${isDragging ? 'is-dragging' : ''} ${className || ''}`}\n >\n {tasks.length === 0 ? (\n <div className=\"picha-upload-content\">\n {icon || (\n <svg className=\"picha-upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12\"></path></svg>\n )}\n <p className=\"picha-upload-text\">\n Drag & drop or <label className=\"picha-upload-link\">\n browse\n <input\n type=\"file\"\n accept=\"image/*\"\n multiple\n ref={fileInputRef}\n onChange={onFileSelect}\n style={{ display: 'none' }}\n />\n </label>\n </p>\n </div>\n ) : (\n <div className=\"picha-uploading-list\">\n {tasks.map(task => (\n <div key={task.id} className=\"picha-task-item\">\n <div className=\"picha-task-info\">\n <span className=\"picha-file-name\">{task.file.name}</span>\n <div className=\"picha-task-actions\" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <span className={`picha-status-text ${task.status}`}>\n {task.status === 'success' ? 'Uploaded' : task.status === 'error' ? 'Failed' : task.status === 'deleting' ? 'Deleting...' : `${task.progress}%`}\n </span>\n {allowDeletion && (task.status === 'success' || task.status === 'deleting') && task.response?.id && (\n <button \n onClick={() => handleDelete(task.id, task.response!.id)}\n className=\"picha-delete-btn\"\n title=\"Delete asset\"\n disabled={task.status === 'deleting'}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"></line><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"></line></svg>\n </button>\n )}\n </div>\n </div>\n <div className=\"picha-progress-bar\">\n <div className={`picha-progress-fill ${task.status}`} style={{ width: `${task.progress}%` }}></div>\n </div>\n {task.error && <p className=\"picha-error-text\">{task.error}</p>}\n </div>\n ))}\n\n <div className=\"picha-add-more\">\n <p className=\"picha-upload-text\">\n <label className=\"picha-upload-link\">\n Upload more files\n <input\n type=\"file\"\n accept=\"image/*\"\n multiple\n onChange={onFileSelect}\n style={{ display: 'none' }}\n />\n </label>\n </p>\n </div>\n </div>\n )}\n </div>\n </>\n );\n};\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,qBAAAE,IAAA,eAAAC,EAAAH,GCAA,IAAAI,EAAwC,iBACxCC,EAA6E,0BA8JzEC,EAAA,6BAhISC,EAAkD,CAAC,CAC9D,OAAAC,EACA,QAAAC,EACA,UAAAC,EACA,KAAAC,EACA,UAAAC,EACA,aAAAC,EACA,QAAAC,EACA,WAAAC,EACA,cAAAC,EACA,SAAAC,EACA,cAAAC,EACA,UAAAC,EACA,SAAAC,EACA,UAAAC,EACA,SAAAC,EACA,KAAAC,CACF,IAAM,CACJ,GAAM,CAACC,EAAYC,CAAa,KAAI,YAAS,EAAK,EAC5C,CAACC,EAAOC,CAAQ,KAAI,YAAuB,CAAC,CAAC,EAC7CC,KAAe,UAAyB,IAAI,EAE5CC,EAAS,IAAI,kBAAgB,CAAE,OAAArB,EAAQ,QAAAC,EAAS,UAAAY,EAAW,SAAAC,EAAU,SAAAF,CAAS,CAAC,EAE/EU,EAAc,MAAOC,GAAkB,CAC3C,GAAIA,EAAM,SAAW,EAAG,OAExB,IAAMC,EAAaD,EAAM,OAAOE,GAAKA,EAAE,KAAK,WAAW,QAAQ,CAAC,EAChE,GAAID,EAAW,SAAW,EAAG,CAC3BlB,IAAU,IAAI,MAAM,iCAAiC,CAAC,EACtD,MACF,CAEA,IAAMoB,EAAeF,EAAW,IAAIG,IAAS,CAC3C,GAAI,OAAO,WAAW,EACtB,KAAAA,EACA,SAAU,EACV,OAAQ,SACV,EAAE,EAEFR,EAASS,GAAQ,CAAC,GAAGA,EAAM,GAAGF,CAAY,CAAC,EAE3C,IAAIG,EAAiB,EACfC,EAAwC,CAAC,EAE/C,MAAM,QAAQ,IAAIJ,EAAa,IAAI,MAAOK,GAAS,CACjD,IAAMC,EAAcC,GAAiC,CACnDd,EAASe,GAAgB,CACvB,IAAMC,EAAQD,EAAa,UAAUE,GAAKA,EAAE,KAAOL,EAAK,EAAE,EAC1D,GAAII,IAAU,GAAI,OAAOD,EACzB,IAAMG,EAAW,CAAC,GAAGH,CAAY,EACjC,OAAAG,EAASF,CAAK,EAAI,CAAE,GAAGE,EAASF,CAAK,EAAG,GAAGF,CAAQ,EAC5CI,CACT,CAAC,CACH,EAEA,GAAI,CACF,IAAMC,EAAgB,QAAM,0BAAuBP,EAAK,IAAI,EAC5DC,EAAW,CAAE,OAAQ,YAAa,KAAMM,CAAc,CAAC,EAEvD,IAAMC,EAAU,CACd,KAAApC,EACA,WAAaqC,GAAc,CACzBR,EAAW,CAAE,SAAUQ,CAAE,CAAC,EAC1BjC,IAAaiC,CAAC,CAChB,CACF,EAEMC,EAAWvC,EACb,MAAMmB,EAAO,aAAaiB,EAAeC,CAAO,EAChD,MAAMlB,EAAO,OAAOiB,EAAeC,CAAO,EAE9CP,EAAW,CAAE,OAAQ,UAAW,SAAU,IAAK,SAAAS,CAAS,CAAC,EACzDX,EAAoB,KAAKW,CAAQ,EACjCrC,IAAYqC,CAAQ,CACtB,OAASC,EAAU,CACjBV,EAAW,CAAE,OAAQ,QAAS,MAAOU,EAAI,SAAW,eAAgB,CAAC,EACrEpC,IAAUoC,CAAG,CACf,QAAE,CACAb,IACIA,IAAmBH,EAAa,QAC9BI,EAAoB,OAAS,GAC/BzB,IAAeyB,CAAmB,CAGxC,CACF,CAAC,CAAC,CACJ,EAEMa,EAAU,GAAuB,CACrC,EAAE,eAAe,EACjB1B,EAAc,EAAK,EACnB,IAAMM,EAAQ,MAAM,KAAK,EAAE,aAAa,OAAS,CAAC,CAAC,EAC/CA,EAAM,QAAQD,EAAYC,CAAK,CACrC,EAEMqB,EAAe,MAAOC,EAAgBC,IAAoB,CAC9D3B,EAAS4B,GAAW,CAClB,IAAMZ,EAAQY,EAAQ,UAAUX,GAAKA,EAAE,KAAOS,CAAM,EACpD,GAAIV,IAAU,GAAI,OAAOY,EACzB,IAAMV,EAAW,CAAC,GAAGU,CAAO,EAC5B,OAAAV,EAASF,CAAK,EAAI,CAAE,GAAGE,EAASF,CAAK,EAAG,OAAQ,UAAW,EACpDE,CACT,CAAC,EAED,GAAI,CACF,MAAMhB,EAAO,YAAYyB,CAAO,EAChC3B,EAAS4B,GAAWA,EAAQ,OAAOX,GAAKA,EAAE,KAAOS,CAAM,CAAC,EACxDpC,IAAWqC,CAAO,CACpB,OAASJ,EAAU,CACjBvB,EAAS4B,GAAW,CAClB,IAAMZ,EAAQY,EAAQ,UAAUX,GAAKA,EAAE,KAAOS,CAAM,EACpD,GAAIV,IAAU,GAAI,OAAOY,EACzB,IAAMV,EAAW,CAAC,GAAGU,CAAO,EAC5B,OAAAV,EAASF,CAAK,EAAI,CAAE,GAAGE,EAASF,CAAK,EAAG,OAAQ,SAAU,EACnDE,CACT,CAAC,EACD3B,IAAgBgC,CAAG,CACrB,CACF,EAEMM,EAAgB,GAA2C,CAC/D,IAAMzB,EAAQ,MAAM,KAAK,EAAE,OAAO,OAAS,CAAC,CAAC,EACzCA,EAAM,QAAQD,EAAYC,CAAK,EACnC,EAAE,OAAO,MAAQ,EACnB,EAEA,SACE,oBACE,oBAAC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA8FN,KAEF,OAAC,OACC,WAAa,GAAM,CAAE,EAAE,eAAe,EAAGN,EAAc,EAAI,CAAG,EAC9D,YAAc,GAAM,CAAE,EAAE,eAAe,EAAGA,EAAc,EAAK,CAAG,EAChE,OAAQ0B,EACR,UAAW,qBAAqB3B,EAAa,cAAgB,EAAE,IAAIL,GAAa,EAAE,GAEjF,SAAAO,EAAM,SAAW,KAChB,QAAC,OAAI,UAAU,uBACZ,UAAAH,MACC,OAAC,OAAI,UAAU,oBAAoB,KAAK,OAAO,OAAO,eAAe,QAAQ,YAAY,mBAAC,QAAK,cAAc,QAAQ,eAAe,QAAQ,YAAY,IAAI,EAAE,iEAAiE,EAAO,KAExO,QAAC,KAAE,UAAU,oBAAoB,+BAChB,QAAC,SAAM,UAAU,oBAAoB,sBAElD,OAAC,SACC,KAAK,OACL,OAAO,UACP,SAAQ,GACR,IAAKK,EACL,SAAU4B,EACV,MAAO,CAAE,QAAS,MAAO,EAC3B,GACF,GACF,GACF,KAEA,QAAC,OAAI,UAAU,uBACZ,UAAA9B,EAAM,IAAIa,MACT,QAAC,OAAkB,UAAU,kBAC3B,qBAAC,OAAI,UAAU,kBACb,oBAAC,QAAK,UAAU,kBAAmB,SAAAA,EAAK,KAAK,KAAK,KAClD,QAAC,OAAI,UAAU,qBAAqB,MAAO,CAAE,QAAS,OAAQ,WAAY,SAAU,IAAK,QAAS,EAChG,oBAAC,QAAK,UAAW,qBAAqBA,EAAK,MAAM,GAC9C,SAAAA,EAAK,SAAW,UAAY,WAAaA,EAAK,SAAW,QAAU,SAAWA,EAAK,SAAW,WAAa,cAAgB,GAAGA,EAAK,QAAQ,IAC9I,EACCvB,IAAkBuB,EAAK,SAAW,WAAaA,EAAK,SAAW,aAAeA,EAAK,UAAU,OAC5F,OAAC,UACC,QAAS,IAAMa,EAAab,EAAK,GAAIA,EAAK,SAAU,EAAE,EACtD,UAAU,mBACV,MAAM,eACN,SAAUA,EAAK,SAAW,WAE1B,oBAAC,OAAI,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OAAO,OAAO,eAAe,YAAY,IAAI,cAAc,QAAQ,eAAe,QAAQ,oBAAC,YAAS,OAAO,eAAe,KAAW,OAAC,QAAK,EAAE,iFAAiF,KAAO,OAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,KAAO,OAAC,QAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAO,EACtX,GAEJ,GACF,KACA,OAAC,OAAI,UAAU,qBACb,mBAAC,OAAI,UAAW,uBAAuBA,EAAK,MAAM,GAAI,MAAO,CAAE,MAAO,GAAGA,EAAK,QAAQ,GAAI,EAAG,EAC/F,EACCA,EAAK,UAAS,OAAC,KAAE,UAAU,mBAAoB,SAAAA,EAAK,MAAM,IAtBnDA,EAAK,EAuBf,CACD,KAED,OAAC,OAAI,UAAU,iBACb,mBAAC,KAAE,UAAU,oBACX,oBAAC,SAAM,UAAU,oBAAoB,iCAEnC,OAAC,SACC,KAAK,OACL,OAAO,UACP,SAAQ,GACR,SAAUiB,EACV,MAAO,CAAE,QAAS,MAAO,EAC3B,GACF,EACF,EACF,GACF,EAEJ,GACF,CAEJ","names":["index_exports","__export","PichaFlowUpload","__toCommonJS","import_react","import_sdk","import_jsx_runtime","PichaFlowUpload","apiKey","baseUrl","useSecure","tags","onSuccess","onSuccessAll","onError","onProgress","allowDeletion","onDelete","onDeleteError","className","tenantId","uploadUrl","fetchUrl","icon","isDragging","setIsDragging","tasks","setTasks","fileInputRef","client","handleFiles","files","imageFiles","f","previewTasks","file","prev","completedCount","successfulResponses","task","updateTask","updates","currentTasks","index","t","newTasks","optimizedFile","options","p","response","err","onDrop","handleDelete","taskId","assetId","current","onFileSelect"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,96 @@
1
+ import{useState as x,useRef as W}from"react";import{PichaFlowClient as B,optimizeImageForUpload as O}from"@pichaflow/sdk";import{Fragment as K,jsx as a,jsxs as n}from"react/jsx-runtime";var V=({apiKey:w,baseUrl:k,useSecure:U,tags:N,onSuccess:D,onSuccessAll:F,onError:m,onProgress:R,allowDeletion:P,onDelete:z,onDeleteError:T,className:C,tenantId:I,uploadUrl:L,fetchUrl:E,icon:$})=>{let[S,f]=x(!1),[b,u]=x([]),A=W(null),h=new B({apiKey:w,baseUrl:k,uploadUrl:L,fetchUrl:E,tenantId:I}),v=async e=>{if(e.length===0)return;let i=e.filter(s=>s.type.startsWith("image/"));if(i.length===0){m?.(new Error("Only image files are supported."));return}let t=i.map(s=>({id:crypto.randomUUID(),file:s,progress:0,status:"pending"}));u(s=>[...s,...t]);let o=0,r=[];await Promise.all(t.map(async s=>{let c=l=>{u(d=>{let p=d.findIndex(H=>H.id===s.id);if(p===-1)return d;let g=[...d];return g[p]={...g[p],...l},g})};try{let l=await O(s.file);c({status:"uploading",file:l});let d={tags:N,onProgress:g=>{c({progress:g}),R?.(g)}},p=U?await h.secureUpload(l,d):await h.upload(l,d);c({status:"success",progress:100,response:p}),r.push(p),D?.(p)}catch(l){c({status:"error",error:l.message||"Upload failed"}),m?.(l)}finally{o++,o===t.length&&r.length>0&&F?.(r)}}))},M=e=>{e.preventDefault(),f(!1);let i=Array.from(e.dataTransfer.files||[]);i.length&&v(i)},j=async(e,i)=>{u(t=>{let o=t.findIndex(s=>s.id===e);if(o===-1)return t;let r=[...t];return r[o]={...r[o],status:"deleting"},r});try{await h.deleteAsset(i),u(t=>t.filter(o=>o.id!==e)),z?.(i)}catch(t){u(o=>{let r=o.findIndex(c=>c.id===e);if(r===-1)return o;let s=[...o];return s[r]={...s[r],status:"success"},s}),T?.(t)}},y=e=>{let i=Array.from(e.target.files||[]);i.length&&v(i),e.target.value=""};return n(K,{children:[a("style",{children:`
2
+ .picha-upload-zone {
3
+ border: 2px dashed #e2e8f0;
4
+ border-radius: 0.75rem;
5
+ padding: 2rem;
6
+ transition: all 0.2s ease;
7
+ background: #f8fafc;
8
+ }
9
+ .picha-upload-zone.is-dragging {
10
+ border-color: #3b82f6;
11
+ background: #eff6ff;
12
+ }
13
+ .picha-upload-content {
14
+ text-align: center;
15
+ }
16
+ .picha-upload-icon { width: 2rem; height: 2rem; color: #94a3b8; margin: 0 auto 1rem; display: block; }
17
+ .picha-upload-text { color: #64748b; font-size: 0.875rem; margin: 0; }
18
+ .picha-upload-link { color: #3b82f6; font-weight: 600; cursor: pointer; }
19
+
20
+ .picha-uploading-list {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: 1rem;
24
+ }
25
+ .picha-task-item {
26
+ background: white;
27
+ padding: 1rem;
28
+ border-radius: 0.5rem;
29
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
30
+ border: 1px solid #f1f5f9;
31
+ }
32
+ .picha-task-info {
33
+ display: flex;
34
+ justify-content: space-between;
35
+ align-items: center;
36
+ margin-bottom: 0.5rem;
37
+ }
38
+ .picha-file-name {
39
+ font-size: 0.875rem;
40
+ font-weight: 500;
41
+ color: #334155;
42
+ white-space: nowrap;
43
+ overflow: hidden;
44
+ text-overflow: ellipsis;
45
+ max-width: 70%;
46
+ }
47
+ .picha-status-text {
48
+ font-size: 0.75rem;
49
+ font-weight: 600;
50
+ color: #64748b;
51
+ }
52
+ .picha-status-text.success { color: #10b981; }
53
+ .picha-status-text.error { color: #ef4444; }
54
+ .picha-status-text.deleting { color: #f59e0b; }
55
+
56
+ .picha-delete-btn {
57
+ background: none;
58
+ border: none;
59
+ color: #94a3b8;
60
+ cursor: pointer;
61
+ padding: 0.25rem;
62
+ border-radius: 0.25rem;
63
+ display: flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ transition: all 0.2s;
67
+ }
68
+ .picha-delete-btn:hover:not(:disabled) {
69
+ color: #ef4444;
70
+ background: #fef2f2;
71
+ }
72
+ .picha-delete-btn:disabled {
73
+ opacity: 0.5;
74
+ cursor: not-allowed;
75
+ }
76
+
77
+ .picha-progress-bar { background: #e2e8f0; height: 0.5rem; border-radius: 999px; overflow: hidden; }
78
+ .picha-progress-fill { background: #3b82f6; height: 100%; transition: width 0.1s ease; }
79
+ .picha-progress-fill.success { background: #10b981; }
80
+ .picha-progress-fill.error { background: #ef4444; }
81
+
82
+ .picha-error-text {
83
+ margin-top: 0.25rem;
84
+ font-size: 0.75rem;
85
+ color: #ef4444;
86
+ }
87
+ .picha-add-more { text-align: center; margin-top: 1.5rem; }
88
+
89
+ @media (prefers-color-scheme: dark) {
90
+ .picha-upload-zone { background: #0f172a; border-color: #1e293b; }
91
+ .picha-task-item { background: #1e293b; border-color: #334155; }
92
+ .picha-file-name { color: #f8fafc; }
93
+ .picha-progress-bar { background: #334155; }
94
+ }
95
+ `}),a("div",{onDragOver:e=>{e.preventDefault(),f(!0)},onDragLeave:e=>{e.preventDefault(),f(!1)},onDrop:M,className:`picha-upload-zone ${S?"is-dragging":""} ${C||""}`,children:b.length===0?n("div",{className:"picha-upload-content",children:[$||a("svg",{className:"picha-upload-icon",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:a("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",d:"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"})}),n("p",{className:"picha-upload-text",children:["Drag & drop or ",n("label",{className:"picha-upload-link",children:["browse",a("input",{type:"file",accept:"image/*",multiple:!0,ref:A,onChange:y,style:{display:"none"}})]})]})]}):n("div",{className:"picha-uploading-list",children:[b.map(e=>n("div",{className:"picha-task-item",children:[n("div",{className:"picha-task-info",children:[a("span",{className:"picha-file-name",children:e.file.name}),n("div",{className:"picha-task-actions",style:{display:"flex",alignItems:"center",gap:"0.5rem"},children:[a("span",{className:`picha-status-text ${e.status}`,children:e.status==="success"?"Uploaded":e.status==="error"?"Failed":e.status==="deleting"?"Deleting...":`${e.progress}%`}),P&&(e.status==="success"||e.status==="deleting")&&e.response?.id&&a("button",{onClick:()=>j(e.id,e.response.id),className:"picha-delete-btn",title:"Delete asset",disabled:e.status==="deleting",children:n("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[a("polyline",{points:"3 6 5 6 21 6"}),a("path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"}),a("line",{x1:"10",y1:"11",x2:"10",y2:"17"}),a("line",{x1:"14",y1:"11",x2:"14",y2:"17"})]})})]})]}),a("div",{className:"picha-progress-bar",children:a("div",{className:`picha-progress-fill ${e.status}`,style:{width:`${e.progress}%`}})}),e.error&&a("p",{className:"picha-error-text",children:e.error})]},e.id)),a("div",{className:"picha-add-more",children:a("p",{className:"picha-upload-text",children:n("label",{className:"picha-upload-link",children:["Upload more files",a("input",{type:"file",accept:"image/*",multiple:!0,onChange:y,style:{display:"none"}})]})})})]})})]})};export{V as PichaFlowUpload};
96
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/PichaFlowUpload.tsx"],"sourcesContent":["import React, { useState, useRef } from 'react';\nimport { PichaFlowClient, type UploadResponse, optimizeImageForUpload } from '@pichaflow/sdk';\n\nexport interface PichaFlowUploadProps {\n apiKey: string;\n baseUrl?: string;\n uploadUrl?: string;\n fetchUrl?: string;\n useSecure?: boolean;\n tags?: string[];\n tenantId?: string;\n onSuccess?: (response: UploadResponse) => void;\n onSuccessAll?: (responses: UploadResponse[]) => void;\n onError?: (error: any) => void;\n onProgress?: (progress: number) => void;\n allowDeletion?: boolean;\n onDelete?: (id: string) => void;\n onDeleteError?: (error: any) => void;\n className?: string;\n icon?: React.ReactNode;\n}\n\ntype UploadTask = {\n id: string;\n file: File;\n progress: number;\n status: 'pending' | 'uploading' | 'success' | 'error' | 'deleting' | 'deleted';\n error?: string;\n response?: UploadResponse;\n};\n\nexport const PichaFlowUpload: React.FC<PichaFlowUploadProps> = ({\n apiKey,\n baseUrl,\n useSecure,\n tags,\n onSuccess,\n onSuccessAll,\n onError,\n onProgress,\n allowDeletion,\n onDelete,\n onDeleteError,\n className,\n tenantId,\n uploadUrl,\n fetchUrl,\n icon\n}) => {\n const [isDragging, setIsDragging] = useState(false);\n const [tasks, setTasks] = useState<UploadTask[]>([]);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const client = new PichaFlowClient({ apiKey, baseUrl, uploadUrl, fetchUrl, tenantId });\n\n const handleFiles = async (files: File[]) => {\n if (files.length === 0) return;\n\n const imageFiles = files.filter(f => f.type.startsWith('image/'));\n if (imageFiles.length === 0) {\n onError?.(new Error('Only image files are supported.'));\n return;\n }\n\n const previewTasks = imageFiles.map(file => ({\n id: crypto.randomUUID(),\n file,\n progress: 0,\n status: 'pending' as const\n }));\n\n setTasks(prev => [...prev, ...previewTasks]);\n\n let completedCount = 0;\n const successfulResponses: UploadResponse[] = [];\n\n await Promise.all(previewTasks.map(async (task) => {\n const updateTask = (updates: Partial<UploadTask>) => {\n setTasks(currentTasks => {\n const index = currentTasks.findIndex(t => t.id === task.id);\n if (index === -1) return currentTasks;\n const newTasks = [...currentTasks];\n newTasks[index] = { ...newTasks[index], ...updates };\n return newTasks;\n });\n };\n\n try {\n const optimizedFile = await optimizeImageForUpload(task.file);\n updateTask({ status: 'uploading', file: optimizedFile });\n\n const options = {\n tags,\n onProgress: (p: number) => {\n updateTask({ progress: p });\n onProgress?.(p);\n }\n };\n\n const response = useSecure\n ? await client.secureUpload(optimizedFile, options)\n : await client.upload(optimizedFile, options);\n\n updateTask({ status: 'success', progress: 100, response });\n successfulResponses.push(response);\n onSuccess?.(response);\n } catch (err: any) {\n updateTask({ status: 'error', error: err.message || 'Upload failed' });\n onError?.(err);\n } finally {\n completedCount++;\n if (completedCount === previewTasks.length) {\n if (successfulResponses.length > 0) {\n onSuccessAll?.(successfulResponses);\n }\n }\n }\n }));\n };\n\n const onDrop = (e: React.DragEvent) => {\n e.preventDefault();\n setIsDragging(false);\n const files = Array.from(e.dataTransfer.files || []) as File[];\n if (files.length) handleFiles(files);\n };\n\n const handleDelete = async (taskId: string, assetId: string) => {\n setTasks(current => {\n const index = current.findIndex(t => t.id === taskId);\n if (index === -1) return current;\n const newTasks = [...current];\n newTasks[index] = { ...newTasks[index], status: 'deleting' };\n return newTasks;\n });\n\n try {\n await client.deleteAsset(assetId);\n setTasks(current => current.filter(t => t.id !== taskId));\n onDelete?.(assetId);\n } catch (err: any) {\n setTasks(current => {\n const index = current.findIndex(t => t.id === taskId);\n if (index === -1) return current;\n const newTasks = [...current];\n newTasks[index] = { ...newTasks[index], status: 'success' };\n return newTasks;\n });\n onDeleteError?.(err);\n }\n };\n\n const onFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(e.target.files || []) as File[];\n if (files.length) handleFiles(files);\n e.target.value = ''; // Reset\n };\n\n return (\n <>\n <style>{`\n .picha-upload-zone {\n border: 2px dashed #e2e8f0;\n border-radius: 0.75rem;\n padding: 2rem;\n transition: all 0.2s ease;\n background: #f8fafc;\n }\n .picha-upload-zone.is-dragging {\n border-color: #3b82f6;\n background: #eff6ff;\n }\n .picha-upload-content {\n text-align: center;\n }\n .picha-upload-icon { width: 2rem; height: 2rem; color: #94a3b8; margin: 0 auto 1rem; display: block; }\n .picha-upload-text { color: #64748b; font-size: 0.875rem; margin: 0; }\n .picha-upload-link { color: #3b82f6; font-weight: 600; cursor: pointer; }\n \n .picha-uploading-list {\n display: flex;\n flex-direction: column;\n gap: 1rem;\n }\n .picha-task-item {\n background: white;\n padding: 1rem;\n border-radius: 0.5rem;\n box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n border: 1px solid #f1f5f9;\n }\n .picha-task-info {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 0.5rem;\n }\n .picha-file-name {\n font-size: 0.875rem;\n font-weight: 500;\n color: #334155;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n max-width: 70%;\n }\n .picha-status-text {\n font-size: 0.75rem;\n font-weight: 600;\n color: #64748b;\n }\n .picha-status-text.success { color: #10b981; }\n .picha-status-text.error { color: #ef4444; }\n .picha-status-text.deleting { color: #f59e0b; }\n \n .picha-delete-btn {\n background: none;\n border: none;\n color: #94a3b8;\n cursor: pointer;\n padding: 0.25rem;\n border-radius: 0.25rem;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s;\n }\n .picha-delete-btn:hover:not(:disabled) {\n color: #ef4444;\n background: #fef2f2;\n }\n .picha-delete-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n \n .picha-progress-bar { background: #e2e8f0; height: 0.5rem; border-radius: 999px; overflow: hidden; }\n .picha-progress-fill { background: #3b82f6; height: 100%; transition: width 0.1s ease; }\n .picha-progress-fill.success { background: #10b981; }\n .picha-progress-fill.error { background: #ef4444; }\n \n .picha-error-text {\n margin-top: 0.25rem;\n font-size: 0.75rem;\n color: #ef4444;\n }\n .picha-add-more { text-align: center; margin-top: 1.5rem; }\n\n @media (prefers-color-scheme: dark) {\n .picha-upload-zone { background: #0f172a; border-color: #1e293b; }\n .picha-task-item { background: #1e293b; border-color: #334155; }\n .picha-file-name { color: #f8fafc; }\n .picha-progress-bar { background: #334155; }\n }\n `}</style>\n\n <div\n onDragOver={(e) => { e.preventDefault(); setIsDragging(true); }}\n onDragLeave={(e) => { e.preventDefault(); setIsDragging(false); }}\n onDrop={onDrop}\n className={`picha-upload-zone ${isDragging ? 'is-dragging' : ''} ${className || ''}`}\n >\n {tasks.length === 0 ? (\n <div className=\"picha-upload-content\">\n {icon || (\n <svg className=\"picha-upload-icon\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12\"></path></svg>\n )}\n <p className=\"picha-upload-text\">\n Drag & drop or <label className=\"picha-upload-link\">\n browse\n <input\n type=\"file\"\n accept=\"image/*\"\n multiple\n ref={fileInputRef}\n onChange={onFileSelect}\n style={{ display: 'none' }}\n />\n </label>\n </p>\n </div>\n ) : (\n <div className=\"picha-uploading-list\">\n {tasks.map(task => (\n <div key={task.id} className=\"picha-task-item\">\n <div className=\"picha-task-info\">\n <span className=\"picha-file-name\">{task.file.name}</span>\n <div className=\"picha-task-actions\" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}>\n <span className={`picha-status-text ${task.status}`}>\n {task.status === 'success' ? 'Uploaded' : task.status === 'error' ? 'Failed' : task.status === 'deleting' ? 'Deleting...' : `${task.progress}%`}\n </span>\n {allowDeletion && (task.status === 'success' || task.status === 'deleting') && task.response?.id && (\n <button \n onClick={() => handleDelete(task.id, task.response!.id)}\n className=\"picha-delete-btn\"\n title=\"Delete asset\"\n disabled={task.status === 'deleting'}\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\"><polyline points=\"3 6 5 6 21 6\"></polyline><path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\"></path><line x1=\"10\" y1=\"11\" x2=\"10\" y2=\"17\"></line><line x1=\"14\" y1=\"11\" x2=\"14\" y2=\"17\"></line></svg>\n </button>\n )}\n </div>\n </div>\n <div className=\"picha-progress-bar\">\n <div className={`picha-progress-fill ${task.status}`} style={{ width: `${task.progress}%` }}></div>\n </div>\n {task.error && <p className=\"picha-error-text\">{task.error}</p>}\n </div>\n ))}\n\n <div className=\"picha-add-more\">\n <p className=\"picha-upload-text\">\n <label className=\"picha-upload-link\">\n Upload more files\n <input\n type=\"file\"\n accept=\"image/*\"\n multiple\n onChange={onFileSelect}\n style={{ display: 'none' }}\n />\n </label>\n </p>\n </div>\n </div>\n )}\n </div>\n </>\n );\n};\n"],"mappings":"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","names":["useState","useRef","PichaFlowClient","optimizeImageForUpload","Fragment","jsx","jsxs","PichaFlowUpload","apiKey","baseUrl","useSecure","tags","onSuccess","onSuccessAll","onError","onProgress","allowDeletion","onDelete","onDeleteError","className","tenantId","uploadUrl","fetchUrl","icon","isDragging","setIsDragging","tasks","setTasks","fileInputRef","client","handleFiles","files","imageFiles","f","previewTasks","file","prev","completedCount","successfulResponses","task","updateTask","updates","currentTasks","index","t","newTasks","optimizedFile","options","p","response","err","onDrop","handleDelete","taskId","assetId","current","onFileSelect"]}
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@pichaflow/react",
3
+ "version": "0.1.0",
4
+ "description": "PichaFlow SDK for React - Edge-native media transformations.",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "nuxt": "./src/index.ts",
12
+ "development": "./src/index.ts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/pichaflow/react-plugin.git"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=18"
27
+ },
28
+ "dependencies": {
29
+ "@pichaflow/sdk": "0.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@testing-library/dom": "^10.4.1",
33
+ "@testing-library/jest-dom": "^6.9.1",
34
+ "@testing-library/react": "^16.3.2",
35
+ "@types/react": "^18.0.0",
36
+ "jsdom": "^29.1.1",
37
+ "vitest": "^4.1.8"
38
+ },
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsup --watch",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest"
44
+ }
45
+ }