@tradly/asset 1.0.12 → 1.0.14
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/README.md +257 -13
- package/dist/components/FileUpload.js +8 -247
- package/dist/components/FileUpload.native.js +13 -0
- package/dist/components/Icons.js +10 -33
- package/dist/components/Icons.native.js +16 -0
- package/dist/components/ImagesSkeleton.js +7 -15
- package/dist/components/ImagesSkeleton.native.js +13 -0
- package/dist/components/MediaGallery.js +8 -148
- package/dist/components/MediaGallery.native.js +13 -0
- package/dist/components/MediaPopup.js +8 -99
- package/dist/components/MediaPopup.native.js +13 -0
- package/dist/components/MediaTab.js +7 -180
- package/dist/components/MediaTab.native.js +13 -0
- package/dist/components/Pagination.js +8 -136
- package/dist/components/Pagination.native.js +13 -0
- package/dist/components/VideosGallery.js +8 -148
- package/dist/components/VideosGallery.native.js +13 -0
- package/dist/core/MediaApiService.js +396 -0
- package/dist/esm/components/FileUpload.js +1 -246
- package/dist/esm/components/FileUpload.native.js +1 -0
- package/dist/esm/components/Icons.js +1 -32
- package/dist/esm/components/Icons.native.js +1 -0
- package/dist/esm/components/ImagesSkeleton.js +1 -14
- package/dist/esm/components/ImagesSkeleton.native.js +1 -0
- package/dist/esm/components/MediaGallery.js +1 -144
- package/dist/esm/components/MediaGallery.native.js +1 -0
- package/dist/esm/components/MediaPopup.js +1 -97
- package/dist/esm/components/MediaPopup.native.js +1 -0
- package/dist/esm/components/MediaTab.js +1 -177
- package/dist/esm/components/MediaTab.native.js +1 -0
- package/dist/esm/components/Pagination.js +1 -134
- package/dist/esm/components/Pagination.native.js +1 -0
- package/dist/esm/components/VideosGallery.js +1 -144
- package/dist/esm/components/VideosGallery.native.js +1 -0
- package/dist/esm/core/MediaApiService.js +390 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/native/FileUpload.native.js +298 -0
- package/dist/esm/native/Icons.native.js +51 -0
- package/dist/esm/native/ImagesSkeleton.native.js +45 -0
- package/dist/esm/native/MediaGallery.native.js +221 -0
- package/dist/esm/native/MediaPopup.native.js +151 -0
- package/dist/esm/native/MediaTab.native.js +175 -0
- package/dist/esm/native/Pagination.native.js +186 -0
- package/dist/esm/native/VideosGallery.native.js +233 -0
- package/dist/esm/services/apiService.js +1 -372
- package/dist/esm/web/FileUpload.web.js +253 -0
- package/dist/esm/web/Icons.web.js +32 -0
- package/dist/esm/web/ImagesSkeleton.web.js +14 -0
- package/dist/esm/web/MediaGallery.web.js +144 -0
- package/dist/esm/web/MediaPopup.web.js +97 -0
- package/dist/esm/web/MediaTab.web.js +177 -0
- package/dist/esm/web/Pagination.web.js +134 -0
- package/dist/esm/web/VideosGallery.web.js +144 -0
- package/dist/index.js +2 -2
- package/dist/native/FileUpload.native.js +304 -0
- package/dist/native/Icons.native.js +59 -0
- package/dist/native/ImagesSkeleton.native.js +52 -0
- package/dist/native/MediaGallery.native.js +230 -0
- package/dist/native/MediaPopup.native.js +158 -0
- package/dist/native/MediaTab.native.js +184 -0
- package/dist/native/Pagination.native.js +193 -0
- package/dist/native/VideosGallery.native.js +241 -0
- package/dist/services/apiService.js +2 -372
- package/dist/web/FileUpload.web.js +259 -0
- package/dist/web/Icons.web.js +39 -0
- package/dist/web/ImagesSkeleton.web.js +21 -0
- package/dist/web/MediaGallery.web.js +153 -0
- package/dist/web/MediaPopup.web.js +104 -0
- package/dist/web/MediaTab.web.js +186 -0
- package/dist/web/Pagination.web.js +141 -0
- package/dist/web/VideosGallery.web.js +153 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -10,9 +10,10 @@ videos, files) with Tradly authentication support.
|
|
|
10
10
|
- 📁 Upload and browse files
|
|
11
11
|
- 🔐 Tradly authentication support
|
|
12
12
|
- 🎨 Customizable UI
|
|
13
|
-
- 📱 Responsive design
|
|
13
|
+
- 📱 Responsive design (Web) + Native mobile support (React Native)
|
|
14
14
|
- 🔄 Pagination support
|
|
15
15
|
- ⚡ Lightweight and performant
|
|
16
|
+
- 🌐 **Cross-platform**: Works on Web (React) and Mobile (React Native)
|
|
16
17
|
|
|
17
18
|
## Installation
|
|
18
19
|
|
|
@@ -24,20 +25,33 @@ yarn add @tradly/asset
|
|
|
24
25
|
|
|
25
26
|
## Peer Dependencies
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
### Web (React)
|
|
28
29
|
|
|
29
30
|
- `react` (>=16.8.0)
|
|
30
31
|
- `react-dom` (>=16.8.0)
|
|
31
|
-
- `@headlessui/react` (^1.7.0) - for Tab components
|
|
32
32
|
- `axios` (^0.24.0) - for API calls
|
|
33
33
|
|
|
34
|
+
### React Native
|
|
35
|
+
|
|
36
|
+
- `react` (>=16.8.0)
|
|
37
|
+
- `react-native` (>=0.60.0)
|
|
38
|
+
- `axios` (^0.24.0) - for API calls
|
|
39
|
+
- **File Picker Library** (choose one):
|
|
40
|
+
- `react-native-image-picker` - Recommended for React Native CLI
|
|
41
|
+
- `expo-image-picker` - For Expo projects
|
|
42
|
+
- Any other picker library that returns `{ uri, type, fileName }` format
|
|
43
|
+
|
|
34
44
|
**Note:**
|
|
35
45
|
|
|
36
46
|
- All pagination logic is built-in, no external pagination library needed!
|
|
37
47
|
- No Tradly SDK dependency - uses direct API calls
|
|
48
|
+
- **React Native**: File picker is configurable - you choose which library to
|
|
49
|
+
use
|
|
38
50
|
|
|
39
51
|
## Basic Usage
|
|
40
52
|
|
|
53
|
+
### Web (React)
|
|
54
|
+
|
|
41
55
|
```jsx
|
|
42
56
|
import React, { useState } from "react";
|
|
43
57
|
import { MediaPopup, MediaApiService } from "@tradly/asset";
|
|
@@ -80,6 +94,154 @@ function MyComponent() {
|
|
|
80
94
|
}
|
|
81
95
|
```
|
|
82
96
|
|
|
97
|
+
### React Native
|
|
98
|
+
|
|
99
|
+
```jsx
|
|
100
|
+
import React, { useState } from "react";
|
|
101
|
+
import { View, Button } from "react-native";
|
|
102
|
+
import { MediaPopup, MediaApiService } from "@tradly/asset";
|
|
103
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
104
|
+
|
|
105
|
+
function MyComponent() {
|
|
106
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
107
|
+
const [selectedMedia, setSelectedMedia] = useState(null);
|
|
108
|
+
|
|
109
|
+
const apiService = new MediaApiService({
|
|
110
|
+
authKey: "your-tradly-auth-key",
|
|
111
|
+
bearerToken: "your-bearer-token",
|
|
112
|
+
environment: "dev", // or "production"
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const handleSelect = (mediaUrl) => {
|
|
116
|
+
setSelectedMedia(mediaUrl);
|
|
117
|
+
setIsOpen(false);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Configure file picker - REQUIRED for React Native
|
|
121
|
+
const picker = (options) => {
|
|
122
|
+
return new Promise((resolve) => {
|
|
123
|
+
launchImageLibrary(options, (response) => {
|
|
124
|
+
resolve(response.assets || []);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<View>
|
|
131
|
+
<Button
|
|
132
|
+
title="Open Media Gallery"
|
|
133
|
+
onPress={() => setIsOpen(true)}
|
|
134
|
+
/>
|
|
135
|
+
|
|
136
|
+
<MediaPopup
|
|
137
|
+
isOpen={isOpen}
|
|
138
|
+
onClose={() => setIsOpen(false)}
|
|
139
|
+
onSelect={handleSelect}
|
|
140
|
+
options={["image", "video"]}
|
|
141
|
+
apiService={apiService}
|
|
142
|
+
picker={picker} // REQUIRED: Your file picker function
|
|
143
|
+
/>
|
|
144
|
+
</View>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## React Native Setup
|
|
150
|
+
|
|
151
|
+
### File Picker Configuration
|
|
152
|
+
|
|
153
|
+
React Native requires you to provide a file picker function. The package
|
|
154
|
+
supports any picker library that returns files in the format
|
|
155
|
+
`{ uri, type, fileName }`.
|
|
156
|
+
|
|
157
|
+
#### Option 1: Using react-native-image-picker
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
npm install react-native-image-picker
|
|
161
|
+
# or
|
|
162
|
+
yarn add react-native-image-picker
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```jsx
|
|
166
|
+
import { launchImageLibrary } from "react-native-image-picker";
|
|
167
|
+
|
|
168
|
+
<MediaPopup
|
|
169
|
+
picker={(options) => {
|
|
170
|
+
return new Promise((resolve) => {
|
|
171
|
+
launchImageLibrary(options, (response) => {
|
|
172
|
+
if (response.didCancel) {
|
|
173
|
+
resolve([]);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
resolve(response.assets || []);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}}
|
|
180
|
+
apiService={apiService}
|
|
181
|
+
// ... other props
|
|
182
|
+
/>;
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Option 2: Using expo-image-picker
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
npx expo install expo-image-picker
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```jsx
|
|
192
|
+
import * as ImagePicker from "expo-image-picker";
|
|
193
|
+
|
|
194
|
+
<MediaPopup
|
|
195
|
+
picker={async (options) => {
|
|
196
|
+
const result = await ImagePicker.launchImageLibraryAsync({
|
|
197
|
+
mediaTypes:
|
|
198
|
+
options.mediaType === "photo"
|
|
199
|
+
? ImagePicker.MediaTypeOptions.Images
|
|
200
|
+
: options.mediaType === "video"
|
|
201
|
+
? ImagePicker.MediaTypeOptions.Videos
|
|
202
|
+
: ImagePicker.MediaTypeOptions.All,
|
|
203
|
+
allowsMultipleSelection: options.allowsMultiple,
|
|
204
|
+
quality: options.quality,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (result.canceled) {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return result.assets || [];
|
|
212
|
+
}}
|
|
213
|
+
apiService={apiService}
|
|
214
|
+
// ... other props
|
|
215
|
+
/>;
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### Option 3: Custom Picker Options
|
|
219
|
+
|
|
220
|
+
You can customize picker options based on the accept type:
|
|
221
|
+
|
|
222
|
+
```jsx
|
|
223
|
+
<MediaPopup
|
|
224
|
+
picker={yourPickerFunction}
|
|
225
|
+
pickerOptions={(accept) => ({
|
|
226
|
+
mediaType: accept?.includes("image") ? "photo" : "video",
|
|
227
|
+
quality: 0.9,
|
|
228
|
+
allowsMultiple: true,
|
|
229
|
+
// Add any other options your picker library supports
|
|
230
|
+
})}
|
|
231
|
+
apiService={apiService}
|
|
232
|
+
// ... other props
|
|
233
|
+
/>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### React Native UI Differences
|
|
237
|
+
|
|
238
|
+
- **Bottom Sheet**: Media gallery opens as a bottom sheet (slides up from
|
|
239
|
+
bottom)
|
|
240
|
+
- **Native Styling**: Uses React Native `StyleSheet` instead of Tailwind CSS
|
|
241
|
+
- **Components**: Uses React Native primitives (`View`, `Text`,
|
|
242
|
+
`TouchableOpacity`, `FlatList`, `Image`)
|
|
243
|
+
- **File Upload**: Requires `picker` prop to be provided
|
|
244
|
+
|
|
83
245
|
## Advanced Usage
|
|
84
246
|
|
|
85
247
|
### Upload Functionality
|
|
@@ -179,19 +341,31 @@ apiService.setBearerToken("new-bearer-token");
|
|
|
179
341
|
|
|
180
342
|
### MediaPopup Props
|
|
181
343
|
|
|
182
|
-
| Prop
|
|
183
|
-
|
|
|
184
|
-
| `isOpen`
|
|
185
|
-
| `onClose`
|
|
186
|
-
| `onSelect`
|
|
187
|
-
| `currentData`
|
|
188
|
-
| `options`
|
|
189
|
-
| `apiService`
|
|
190
|
-
| `onError`
|
|
191
|
-
| `title`
|
|
344
|
+
| Prop | Type | Default | Description | Platform |
|
|
345
|
+
| --------------- | ----------------- | ----------------- | --------------------------------------------------- | ------------ |
|
|
346
|
+
| `isOpen` | `boolean` | `false` | Controls popup visibility | Web & Native |
|
|
347
|
+
| `onClose` | `function` | - | Callback when popup closes | Web & Native |
|
|
348
|
+
| `onSelect` | `function` | - | Callback when media is selected | Web & Native |
|
|
349
|
+
| `currentData` | `any` | - | Currently selected media data | Web & Native |
|
|
350
|
+
| `options` | `array` | `['image']` | Media types to show: `'image'`, `'video'`, `'file'` | Web & Native |
|
|
351
|
+
| `apiService` | `MediaApiService` | - | **Required**: API service instance | Web & Native |
|
|
352
|
+
| `onError` | `function` | - | Error handler callback | Web & Native |
|
|
353
|
+
| `title` | `string` | `'Media Gallery'` | Popup title | Web & Native |
|
|
354
|
+
| `picker` | `function` | - | **Required for React Native**: File picker function | Native only |
|
|
355
|
+
| `pickerOptions` | `function` | - | Optional: Function to generate picker options | Native only |
|
|
356
|
+
|
|
357
|
+
**React Native Required Props:**
|
|
358
|
+
|
|
359
|
+
- `picker`: A function that accepts options and returns a Promise resolving to
|
|
360
|
+
an array of file objects
|
|
361
|
+
- Each file object should have:
|
|
362
|
+
`{ uri: string, type?: string, fileName?: string }`
|
|
363
|
+
- Example: `picker={(options) => launchImageLibrary(options, callback)}`
|
|
192
364
|
|
|
193
365
|
## Styling
|
|
194
366
|
|
|
367
|
+
### Web (React)
|
|
368
|
+
|
|
195
369
|
This package uses Tailwind CSS classes and supports full customization at every
|
|
196
370
|
component level. All components accept `className` props that allow you to
|
|
197
371
|
override default styles.
|
|
@@ -237,6 +411,38 @@ override default styles.
|
|
|
237
411
|
**Individual Components:** You can also style individual components when using
|
|
238
412
|
them separately. See `STYLING_GUIDE.md` for complete documentation.
|
|
239
413
|
|
|
414
|
+
### React Native
|
|
415
|
+
|
|
416
|
+
React Native components use `StyleSheet` for styling. You can customize styles
|
|
417
|
+
using style props:
|
|
418
|
+
|
|
419
|
+
```jsx
|
|
420
|
+
<MediaPopup
|
|
421
|
+
apiService={apiService}
|
|
422
|
+
picker={picker}
|
|
423
|
+
// Customize styles
|
|
424
|
+
containerStyle={{ backgroundColor: "#F5F5F5" }}
|
|
425
|
+
headerStyle={{ padding: 20 }}
|
|
426
|
+
titleStyle={{ fontSize: 28, color: "#000" }}
|
|
427
|
+
// ... other props
|
|
428
|
+
/>
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
**Available Style Props (React Native):**
|
|
432
|
+
|
|
433
|
+
- `containerStyle` - Main popup container
|
|
434
|
+
- `headerStyle` - Header container
|
|
435
|
+
- `titleStyle` - Title text
|
|
436
|
+
- `closeButtonStyle` - Close button
|
|
437
|
+
- `tabListStyle` - Tab list
|
|
438
|
+
- `tabButtonStyle` - Base tab button
|
|
439
|
+
- `tabButtonActiveStyle` - Active tab button
|
|
440
|
+
- `tabButtonInactiveStyle` - Inactive tab button
|
|
441
|
+
- `gridStyle` - Media grid layout
|
|
442
|
+
- `imageItemStyle` - Image item styles
|
|
443
|
+
- `videoItemStyle` - Video item styles
|
|
444
|
+
- `paginationContainerStyle` - Pagination container
|
|
445
|
+
|
|
240
446
|
### Full Styling Guide
|
|
241
447
|
|
|
242
448
|
For detailed styling documentation with examples, see
|
|
@@ -270,6 +476,8 @@ For detailed styling documentation with examples, see
|
|
|
270
476
|
|
|
271
477
|
### All Media Types
|
|
272
478
|
|
|
479
|
+
**Web:**
|
|
480
|
+
|
|
273
481
|
```jsx
|
|
274
482
|
<MediaPopup
|
|
275
483
|
isOpen={isOpen}
|
|
@@ -280,6 +488,36 @@ For detailed styling documentation with examples, see
|
|
|
280
488
|
/>
|
|
281
489
|
```
|
|
282
490
|
|
|
491
|
+
**React Native:**
|
|
492
|
+
|
|
493
|
+
```jsx
|
|
494
|
+
<MediaPopup
|
|
495
|
+
isOpen={isOpen}
|
|
496
|
+
onClose={() => setIsOpen(false)}
|
|
497
|
+
onSelect={handleSelect}
|
|
498
|
+
options={["image", "video", "file"]}
|
|
499
|
+
apiService={apiService}
|
|
500
|
+
picker={picker} // Required
|
|
501
|
+
/>
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
## Platform-Specific Notes
|
|
505
|
+
|
|
506
|
+
### Web (React)
|
|
507
|
+
|
|
508
|
+
- Uses `react-dom` for portal rendering
|
|
509
|
+
- Tailwind CSS for styling
|
|
510
|
+
- HTML file input for file selection
|
|
511
|
+
- Canvas API for image compression
|
|
512
|
+
|
|
513
|
+
### React Native
|
|
514
|
+
|
|
515
|
+
- Uses `Modal` component for bottom sheet
|
|
516
|
+
- React Native `StyleSheet` for styling
|
|
517
|
+
- Requires file picker library (configurable)
|
|
518
|
+
- Uses `FlatList` for efficient media rendering
|
|
519
|
+
- Supports both iOS and Android
|
|
520
|
+
|
|
283
521
|
## Development
|
|
284
522
|
|
|
285
523
|
To develop or modify this package:
|
|
@@ -290,6 +528,12 @@ npm install
|
|
|
290
528
|
npm run dev
|
|
291
529
|
```
|
|
292
530
|
|
|
531
|
+
**Note:** The package automatically detects the platform and uses the
|
|
532
|
+
appropriate components:
|
|
533
|
+
|
|
534
|
+
- Web bundlers will use `.web.jsx` files
|
|
535
|
+
- React Native bundlers (Metro) will use `.native.jsx` files
|
|
536
|
+
|
|
293
537
|
## License
|
|
294
538
|
|
|
295
539
|
MIT
|
|
@@ -1,252 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
3
|
Object.defineProperty(exports, "__esModule", {
|
|
5
4
|
value: true
|
|
6
5
|
});
|
|
7
|
-
exports
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function
|
|
15
|
-
function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; }
|
|
16
|
-
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
|
|
17
|
-
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
18
|
-
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
19
|
-
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
20
|
-
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
21
|
-
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } // Helper: compress image on the client before upload
|
|
22
|
-
// - Downscale to fit within maxWidth/maxHeight.
|
|
23
|
-
// - Convert raster images (JPEG/PNG, etc.) to WebP for better compression.
|
|
24
|
-
// - Skip SVG (vector) to avoid degradation.
|
|
25
|
-
var compressImage = function compressImage(file) {
|
|
26
|
-
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
27
|
-
_ref$maxWidth = _ref.maxWidth,
|
|
28
|
-
maxWidth = _ref$maxWidth === void 0 ? 1600 : _ref$maxWidth,
|
|
29
|
-
_ref$maxHeight = _ref.maxHeight,
|
|
30
|
-
maxHeight = _ref$maxHeight === void 0 ? 1600 : _ref$maxHeight,
|
|
31
|
-
_ref$quality = _ref.quality,
|
|
32
|
-
quality = _ref$quality === void 0 ? 1 : _ref$quality,
|
|
33
|
-
_ref$convertToWebp = _ref.convertToWebp,
|
|
34
|
-
convertToWebp = _ref$convertToWebp === void 0 ? true : _ref$convertToWebp;
|
|
35
|
-
return new Promise(function (resolve) {
|
|
36
|
-
// Non-image or SVG: skip compression to avoid losing vector quality
|
|
37
|
-
if (!file.type.startsWith("image/") || file.type === "image/svg+xml") {
|
|
38
|
-
resolve(file);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
var img = new Image();
|
|
42
|
-
img.onload = function () {
|
|
43
|
-
var width = img.width,
|
|
44
|
-
height = img.height;
|
|
45
|
-
|
|
46
|
-
// If the image is already small, keep it as-is
|
|
47
|
-
if (width <= maxWidth && height <= maxHeight) {
|
|
48
|
-
resolve(file);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
var scale = Math.min(maxWidth / width, maxHeight / height);
|
|
52
|
-
var targetWidth = Math.round(width * scale);
|
|
53
|
-
var targetHeight = Math.round(height * scale);
|
|
54
|
-
var canvas = document.createElement("canvas");
|
|
55
|
-
canvas.width = targetWidth;
|
|
56
|
-
canvas.height = targetHeight;
|
|
57
|
-
var ctx = canvas.getContext("2d");
|
|
58
|
-
if (!ctx) {
|
|
59
|
-
resolve(file);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
|
|
63
|
-
|
|
64
|
-
// Prefer WebP for raster images to reduce size (where supported)
|
|
65
|
-
// Keep original type for GIF to avoid breaking animation.
|
|
66
|
-
var isRaster = file.type === "image/jpeg" || file.type === "image/jpg" || file.type === "image/png" || file.type === "image/webp" || file.type === "image/heic" || file.type === "image/heif";
|
|
67
|
-
var targetType = convertToWebp && isRaster && file.type !== "image/gif" ? "image/webp" : file.type;
|
|
68
|
-
canvas.toBlob(function (blob) {
|
|
69
|
-
if (!blob) {
|
|
70
|
-
resolve(file);
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
var compressedFile = new File([blob], file.name, {
|
|
74
|
-
type: targetType,
|
|
75
|
-
lastModified: Date.now()
|
|
76
|
-
});
|
|
77
|
-
resolve(compressedFile);
|
|
78
|
-
}, targetType, quality);
|
|
79
|
-
};
|
|
80
|
-
img.onerror = function () {
|
|
81
|
-
resolve(file);
|
|
82
|
-
};
|
|
83
|
-
var reader = new FileReader();
|
|
84
|
-
reader.onload = function (event) {
|
|
85
|
-
var _event$target;
|
|
86
|
-
img.src = (_event$target = event.target) === null || _event$target === void 0 ? void 0 : _event$target.result;
|
|
87
|
-
};
|
|
88
|
-
reader.onerror = function () {
|
|
89
|
-
return resolve(file);
|
|
90
|
-
};
|
|
91
|
-
reader.readAsDataURL(file);
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
var FileUpload = function FileUpload(_ref2) {
|
|
95
|
-
var loadMedia = _ref2.loadMedia,
|
|
96
|
-
accept = _ref2.accept,
|
|
97
|
-
title = _ref2.title,
|
|
98
|
-
apiService = _ref2.apiService,
|
|
99
|
-
onUploadStart = _ref2.onUploadStart,
|
|
100
|
-
onUploadComplete = _ref2.onUploadComplete,
|
|
101
|
-
onUploadError = _ref2.onUploadError,
|
|
102
|
-
className = _ref2.className,
|
|
103
|
-
buttonClassName = _ref2.buttonClassName,
|
|
104
|
-
iconContainerClassName = _ref2.iconContainerClassName,
|
|
105
|
-
titleClassName = _ref2.titleClassName,
|
|
106
|
-
loadingClassName = _ref2.loadingClassName;
|
|
107
|
-
var _useState = (0, _react.useState)([]),
|
|
108
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
109
|
-
files = _useState2[0],
|
|
110
|
-
setFiles = _useState2[1];
|
|
111
|
-
var _useState3 = (0, _react.useState)(false),
|
|
112
|
-
_useState4 = _slicedToArray(_useState3, 2),
|
|
113
|
-
isLoading = _useState4[0],
|
|
114
|
-
setISLoading = _useState4[1];
|
|
115
|
-
var _useState5 = (0, _react.useState)(0),
|
|
116
|
-
_useState6 = _slicedToArray(_useState5, 2),
|
|
117
|
-
length = _useState6[0],
|
|
118
|
-
setLength = _useState6[1];
|
|
119
|
-
|
|
120
|
-
// Upload files
|
|
121
|
-
var uploadFiles = /*#__PURE__*/function () {
|
|
122
|
-
var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(fileList) {
|
|
123
|
-
var filesToUpload, uploadedUrls, _t;
|
|
124
|
-
return _regenerator().w(function (_context) {
|
|
125
|
-
while (1) switch (_context.p = _context.n) {
|
|
126
|
-
case 0:
|
|
127
|
-
if (onUploadStart) {
|
|
128
|
-
onUploadStart(fileList);
|
|
129
|
-
}
|
|
130
|
-
setISLoading(true);
|
|
131
|
-
setLength(fileList.length);
|
|
132
|
-
_context.p = 1;
|
|
133
|
-
filesToUpload = fileList; // For image uploads, compress on the client before sending
|
|
134
|
-
if (!(accept && accept.includes("image"))) {
|
|
135
|
-
_context.n = 3;
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
_context.n = 2;
|
|
139
|
-
return Promise.all(fileList.map(function (file) {
|
|
140
|
-
return compressImage(file, {
|
|
141
|
-
maxWidth: 1600,
|
|
142
|
-
maxHeight: 1600,
|
|
143
|
-
quality: 0.95,
|
|
144
|
-
convertToWebp: true
|
|
145
|
-
});
|
|
146
|
-
}));
|
|
147
|
-
case 2:
|
|
148
|
-
filesToUpload = _context.v;
|
|
149
|
-
case 3:
|
|
150
|
-
_context.n = 4;
|
|
151
|
-
return apiService.uploadMedia(filesToUpload, apiService.authKey);
|
|
152
|
-
case 4:
|
|
153
|
-
uploadedUrls = _context.v;
|
|
154
|
-
if (onUploadComplete) {
|
|
155
|
-
onUploadComplete(uploadedUrls);
|
|
156
|
-
}
|
|
157
|
-
setLength(0);
|
|
158
|
-
if (loadMedia) {
|
|
159
|
-
loadMedia();
|
|
160
|
-
}
|
|
161
|
-
_context.n = 6;
|
|
162
|
-
break;
|
|
163
|
-
case 5:
|
|
164
|
-
_context.p = 5;
|
|
165
|
-
_t = _context.v;
|
|
166
|
-
console.error("Upload error:", _t);
|
|
167
|
-
if (onUploadError) {
|
|
168
|
-
onUploadError(_t);
|
|
169
|
-
}
|
|
170
|
-
case 6:
|
|
171
|
-
_context.p = 6;
|
|
172
|
-
setISLoading(false);
|
|
173
|
-
return _context.f(6);
|
|
174
|
-
case 7:
|
|
175
|
-
return _context.a(2);
|
|
176
|
-
}
|
|
177
|
-
}, _callee, null, [[1, 5, 6, 7]]);
|
|
178
|
-
}));
|
|
179
|
-
return function uploadFiles(_x) {
|
|
180
|
-
return _ref3.apply(this, arguments);
|
|
181
|
-
};
|
|
182
|
-
}();
|
|
183
|
-
|
|
184
|
-
// Default classes with customization support
|
|
185
|
-
var defaultContainerClass = "min-w-40 h-40";
|
|
186
|
-
var defaultButtonClass = "w-full h-full flex flex-col justify-center items-center text-sm border border-primary border-dashed rounded-lg hover:bg-gray-50 transition-colors";
|
|
187
|
-
var defaultIconContainerClass = "p-[10px] bg-primary rounded-full";
|
|
188
|
-
var defaultTitleClass = "mt-2";
|
|
189
|
-
var defaultLoadingClass = "flex items-center justify-center h-40 bg-gray-200 rounded-lg shadow-md animate-pulse";
|
|
190
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
191
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
192
|
-
className: className || defaultContainerClass,
|
|
193
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("input", {
|
|
194
|
-
required: true,
|
|
195
|
-
id: "media_select_".concat(files === null || files === void 0 ? void 0 : files.length),
|
|
196
|
-
type: "file",
|
|
197
|
-
className: "hidden",
|
|
198
|
-
accept: accept,
|
|
199
|
-
placeholder: "",
|
|
200
|
-
onChange: (/*#__PURE__*/function () {
|
|
201
|
-
var _ref4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(e) {
|
|
202
|
-
var all_files;
|
|
203
|
-
return _regenerator().w(function (_context2) {
|
|
204
|
-
while (1) switch (_context2.n) {
|
|
205
|
-
case 0:
|
|
206
|
-
e.stopPropagation();
|
|
207
|
-
all_files = Array.from(e.target.files || []);
|
|
208
|
-
if (!((all_files === null || all_files === void 0 ? void 0 : all_files.length) > 0)) {
|
|
209
|
-
_context2.n = 1;
|
|
210
|
-
break;
|
|
211
|
-
}
|
|
212
|
-
_context2.n = 1;
|
|
213
|
-
return uploadFiles(all_files);
|
|
214
|
-
case 1:
|
|
215
|
-
// Reset input
|
|
216
|
-
e.target.value = "";
|
|
217
|
-
case 2:
|
|
218
|
-
return _context2.a(2);
|
|
219
|
-
}
|
|
220
|
-
}, _callee2);
|
|
221
|
-
}));
|
|
222
|
-
return function (_x2) {
|
|
223
|
-
return _ref4.apply(this, arguments);
|
|
224
|
-
};
|
|
225
|
-
}()),
|
|
226
|
-
multiple: true
|
|
227
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("button", {
|
|
228
|
-
type: "button",
|
|
229
|
-
className: buttonClassName || defaultButtonClass,
|
|
230
|
-
onClick: function onClick() {
|
|
231
|
-
return document.getElementById("media_select_".concat(files === null || files === void 0 ? void 0 : files.length)).click();
|
|
232
|
-
},
|
|
233
|
-
disabled: isLoading,
|
|
234
|
-
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
235
|
-
className: iconContainerClassName || defaultIconContainerClass,
|
|
236
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Icons.CameraIcon, {})
|
|
237
|
-
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
238
|
-
className: titleClassName || defaultTitleClass,
|
|
239
|
-
children: title
|
|
240
|
-
})]
|
|
241
|
-
})]
|
|
242
|
-
}), isLoading && Array.from({
|
|
243
|
-
length: length
|
|
244
|
-
}).map(function (_, index) {
|
|
245
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
|
|
246
|
-
className: loadingClassName || defaultLoadingClass,
|
|
247
|
-
children: ["File Uploading... ", index + 1, "/", length]
|
|
248
|
-
}, index);
|
|
249
|
-
})]
|
|
250
|
-
});
|
|
251
|
-
};
|
|
252
|
-
var _default = exports.default = FileUpload;
|
|
6
|
+
Object.defineProperty(exports, "default", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _FileUpload.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _FileUpload = _interopRequireDefault(require("../web/FileUpload.web"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "default", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _FileUpload.default;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _FileUpload = _interopRequireDefault(require("../native/FileUpload.native"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
package/dist/components/Icons.js
CHANGED
|
@@ -3,37 +3,14 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
fill: "none",
|
|
16
|
-
viewBox: "0 0 24 24",
|
|
17
|
-
strokeWidth: "1.5",
|
|
18
|
-
stroke: "currentColor",
|
|
19
|
-
className: className,
|
|
20
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
21
|
-
strokeLinecap: "round",
|
|
22
|
-
strokeLinejoin: "round",
|
|
23
|
-
d: "M6 18L18 6M6 6l12 12"
|
|
24
|
-
})
|
|
6
|
+
var _Icons = require("../web/Icons.web");
|
|
7
|
+
Object.keys(_Icons).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _Icons[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function get() {
|
|
13
|
+
return _Icons[key];
|
|
14
|
+
}
|
|
25
15
|
});
|
|
26
|
-
};
|
|
27
|
-
var CameraIcon = exports.CameraIcon = function CameraIcon() {
|
|
28
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)("svg", {
|
|
29
|
-
width: "22",
|
|
30
|
-
height: "18",
|
|
31
|
-
viewBox: "0 0 22 20",
|
|
32
|
-
fill: "none",
|
|
33
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
34
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("path", {
|
|
35
|
-
d: "M7.75033 0.166672L5.76783 2.33334H2.33366C1.14199 2.33334 0.166992 3.30834 0.166992 4.50001V17.5C0.166992 18.6917 1.14199 19.6667 2.33366 19.6667H19.667C20.8587 19.6667 21.8337 18.6917 21.8337 17.5V4.50001C21.8337 3.30834 20.8587 2.33334 19.667 2.33334H16.2328L14.2503 0.166672H7.75033ZM11.0003 16.4167C8.01033 16.4167 5.58366 13.99 5.58366 11C5.58366 8.01001 8.01033 5.58334 11.0003 5.58334C13.9903 5.58334 16.417 8.01001 16.417 11C16.417 13.99 13.9903 16.4167 11.0003 16.4167Z",
|
|
36
|
-
fill: "white"
|
|
37
|
-
})
|
|
38
|
-
});
|
|
39
|
-
};
|
|
16
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _Icons = require("../native/Icons.native");
|
|
7
|
+
Object.keys(_Icons).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _Icons[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function get() {
|
|
13
|
+
return _Icons[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|