@vinaup/media-ui 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of @vinaup/media-ui might be problematic. Click here for more details.

package/dist/index.js ADDED
@@ -0,0 +1,896 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ MediaDetail: () => MediaDetail,
24
+ MediaGrid: () => MediaGrid,
25
+ MediaUpload: () => MediaUpload,
26
+ cx: () => cx,
27
+ formatFileSize: () => formatFileSize,
28
+ validateImageFile: () => validateImageFile
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/components/MediaUpload.tsx
33
+ var import_styles = require("@mantine/dropzone/styles.css");
34
+ var import_core = require("@mantine/core");
35
+ var import_dropzone = require("@mantine/dropzone");
36
+ var import_tb = require("react-icons/tb");
37
+ var import_hi = require("react-icons/hi");
38
+ var import_react = require("react");
39
+ var import_notifications = require("@mantine/notifications");
40
+
41
+ // src/utils/helpers.ts
42
+ var validateImageFile = (file) => {
43
+ const validTypes = ["image/png", "image/jpeg", "image/jpg"];
44
+ return validTypes.includes(file.type);
45
+ };
46
+ var formatFileSize = (bytes) => {
47
+ if (bytes === 0) return "0 Bytes";
48
+ const k = 1024;
49
+ const sizes = ["Bytes", "KB", "MB"];
50
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
51
+ return Math.round(bytes / Math.pow(k, i) * 100) / 100 + " " + sizes[i];
52
+ };
53
+ var cx = (...classNames) => {
54
+ const validClassNames = classNames.filter(Boolean);
55
+ const result = validClassNames.join(" ");
56
+ return result;
57
+ };
58
+
59
+ // src/components/styles/media-upload.module.scss
60
+ var media_upload_module_default = {};
61
+
62
+ // src/components/MediaUpload.tsx
63
+ var import_jsx_runtime = require("react/jsx-runtime");
64
+ function MediaUpload({
65
+ onUpload,
66
+ onSave,
67
+ onUploadSuccess,
68
+ onUploadError,
69
+ maxSize = 2 * 1024 ** 2,
70
+ acceptedTypes = ["image/png", "image/jpeg", "image/jpg"],
71
+ multiple = true,
72
+ folder = "media",
73
+ classNames
74
+ }) {
75
+ const [isUploading, setIsUploading] = (0, import_react.useState)(false);
76
+ const [uploadFiles, setUploadFiles] = (0, import_react.useState)([]);
77
+ const handleDrop = async (files) => {
78
+ if (files.length === 0) return;
79
+ const invalidFiles = files.filter((file) => !validateImageFile(file));
80
+ if (invalidFiles.length > 0) {
81
+ import_notifications.notifications.show({
82
+ title: "Invalid file type",
83
+ message: `${invalidFiles.length} file(s) have invalid type.`,
84
+ color: "red"
85
+ });
86
+ return;
87
+ }
88
+ setIsUploading(true);
89
+ const newUploadFiles = files.map((file, index) => ({
90
+ id: `${Date.now()}-${index}`,
91
+ file,
92
+ preview: URL.createObjectURL(file),
93
+ status: "uploading"
94
+ }));
95
+ setUploadFiles((prev) => [...newUploadFiles, ...prev]);
96
+ try {
97
+ const uploadResults = await onUpload(files);
98
+ setUploadFiles(
99
+ (prev) => prev.map((item) => {
100
+ const idx = newUploadFiles.findIndex((f) => f.id === item.id);
101
+ if (idx !== -1 && uploadResults[idx]) {
102
+ return { ...item, status: "success", url: uploadResults[idx].url };
103
+ }
104
+ return item;
105
+ })
106
+ );
107
+ if (onSave) {
108
+ const mediaData = uploadResults.map((result) => ({
109
+ name: result.name,
110
+ title: null,
111
+ description: null,
112
+ url: result.url,
113
+ type: "image",
114
+ folder
115
+ }));
116
+ const savedMedia = await onSave(mediaData);
117
+ onUploadSuccess?.(savedMedia);
118
+ }
119
+ import_notifications.notifications.show({
120
+ title: "Upload successful",
121
+ message: `Successfully uploaded ${uploadResults.length} image(s)`,
122
+ color: "green"
123
+ });
124
+ } catch (error) {
125
+ setUploadFiles(
126
+ (prev) => prev.map(
127
+ (item) => newUploadFiles.some((f) => f.id === item.id) ? { ...item, status: "error", error: error.message } : item
128
+ )
129
+ );
130
+ onUploadError?.(error);
131
+ import_notifications.notifications.show({
132
+ title: "Upload failed",
133
+ message: error.message,
134
+ color: "red"
135
+ });
136
+ } finally {
137
+ setIsUploading(false);
138
+ }
139
+ };
140
+ const handleReject = (files) => {
141
+ import_notifications.notifications.show({
142
+ title: "Files rejected",
143
+ message: `${files.length} file(s) were rejected. Please check file type and size (\u2264 2MB).`,
144
+ color: "red"
145
+ });
146
+ };
147
+ (0, import_react.useEffect)(() => {
148
+ return () => {
149
+ uploadFiles.forEach((item) => {
150
+ if (item.preview.startsWith("blob:")) {
151
+ URL.revokeObjectURL(item.preview);
152
+ }
153
+ });
154
+ };
155
+ }, [uploadFiles]);
156
+ const handleCopyLink = async (url) => {
157
+ try {
158
+ await navigator.clipboard.writeText(url);
159
+ import_notifications.notifications.show({
160
+ title: "Link copied",
161
+ message: "Image URL has been copied to clipboard",
162
+ color: "green"
163
+ });
164
+ } catch (error) {
165
+ import_notifications.notifications.show({
166
+ title: "Copy failed",
167
+ message: error instanceof Error ? error.message : "Failed to copy link to clipboard",
168
+ color: "red"
169
+ });
170
+ }
171
+ };
172
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
173
+ import_core.Stack,
174
+ {
175
+ gap: "lg",
176
+ classNames: {
177
+ root: classNames?.rootStack?.root
178
+ },
179
+ children: [
180
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
181
+ import_dropzone.Dropzone,
182
+ {
183
+ onDrop: handleDrop,
184
+ onReject: handleReject,
185
+ maxSize,
186
+ accept: acceptedTypes,
187
+ disabled: isUploading,
188
+ multiple,
189
+ classNames: classNames?.dropzone,
190
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
191
+ import_core.Group,
192
+ {
193
+ justify: "center",
194
+ gap: "xl",
195
+ mih: 220,
196
+ classNames: {
197
+ root: classNames?.dropzoneGroup?.root
198
+ },
199
+ children: [
200
+ isUploading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.Loader, { size: 52 }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropzone.DropzoneAccept, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tb.TbUpload, { size: 52, color: "blue" }) }),
202
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropzone.DropzoneReject, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_hi.HiOutlineX, { size: 52, color: "red" }) }),
203
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dropzone.DropzoneIdle, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_tb.TbPhoto, { size: 52 }) })
204
+ ] }),
205
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
206
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
207
+ import_core.Text,
208
+ {
209
+ size: "xl",
210
+ inline: true,
211
+ classNames: {
212
+ root: classNames?.dropzoneText?.root
213
+ },
214
+ children: "Drag images here or click to select files"
215
+ }
216
+ ),
217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
218
+ import_core.Text,
219
+ {
220
+ size: "sm",
221
+ c: "dimmed",
222
+ inline: true,
223
+ mt: 7,
224
+ classNames: {
225
+ root: classNames?.dropzoneSubtext?.root
226
+ },
227
+ children: "(png, jpg, jpeg; Size \u2264 2M)"
228
+ }
229
+ )
230
+ ] })
231
+ ]
232
+ }
233
+ )
234
+ }
235
+ ),
236
+ uploadFiles.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
237
+ import_core.Stack,
238
+ {
239
+ gap: "xs",
240
+ classNames: {
241
+ root: classNames?.recentStack?.root
242
+ },
243
+ children: [
244
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
245
+ import_core.Text,
246
+ {
247
+ fw: 500,
248
+ classNames: {
249
+ root: cx(media_upload_module_default.sectionTitleRoot, classNames?.sectionTitle?.root)
250
+ },
251
+ children: "Recently Uploaded Images"
252
+ }
253
+ ),
254
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.Grid, { classNames: classNames?.grid, children: uploadFiles.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.GridCol, { span: { base: 6, sm: 6, md: 3, lg: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
255
+ import_core.Paper,
256
+ {
257
+ p: "sm",
258
+ withBorder: true,
259
+ radius: "md",
260
+ classNames: {
261
+ root: classNames?.itemPaper?.root
262
+ },
263
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
264
+ import_core.Stack,
265
+ {
266
+ gap: 6,
267
+ classNames: {
268
+ root: classNames?.itemStack?.root
269
+ },
270
+ children: [
271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cx(media_upload_module_default.imageContainer, classNames?.imageContainer), children: [
272
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
273
+ import_core.Image,
274
+ {
275
+ src: item.preview,
276
+ alt: item.file.name,
277
+ fit: "cover",
278
+ classNames: {
279
+ root: cx(media_upload_module_default.itemImageRoot, classNames?.itemImage?.root)
280
+ }
281
+ }
282
+ ),
283
+ item.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx(media_upload_module_default.uploadingOverlay, classNames?.uploadingOverlay), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.Loader, { size: "sm", color: "white" }) }),
284
+ item.status === "success" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx(
285
+ media_upload_module_default.statusBadge,
286
+ media_upload_module_default.statusBadgeSuccess,
287
+ classNames?.statusBadge,
288
+ classNames?.statusBadgeSuccess
289
+ ), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.Text, { size: "xs", c: "white", fw: 700, children: "\u2713" }) }),
290
+ item.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cx(
291
+ media_upload_module_default.statusBadge,
292
+ media_upload_module_default.statusBadgeError,
293
+ classNames?.statusBadge,
294
+ classNames?.statusBadgeError
295
+ ), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_hi.HiOutlineX, { size: 16, color: "white" }) })
296
+ ] }),
297
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
298
+ import_core.Text,
299
+ {
300
+ size: "xs",
301
+ c: "dimmed",
302
+ lineClamp: 1,
303
+ title: item.file.name,
304
+ classNames: {
305
+ root: classNames?.itemFilename?.root
306
+ },
307
+ children: item.file.name
308
+ }
309
+ ),
310
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
311
+ import_core.Group,
312
+ {
313
+ justify: "space-between",
314
+ align: "end",
315
+ gap: "xs",
316
+ classNames: {
317
+ root: classNames?.itemGroup?.root
318
+ },
319
+ children: [
320
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
321
+ import_core.Text,
322
+ {
323
+ size: "xs",
324
+ c: "dimmed",
325
+ classNames: {
326
+ root: classNames?.itemFilesize?.root
327
+ },
328
+ children: formatFileSize(item.file.size)
329
+ }
330
+ ),
331
+ item.status === "success" && item.url && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
332
+ import_core.UnstyledButton,
333
+ {
334
+ onClick: () => handleCopyLink(item.url),
335
+ classNames: {
336
+ root: cx(media_upload_module_default.copyButtonRoot, classNames?.copyButton?.root)
337
+ },
338
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
339
+ import_core.Text,
340
+ {
341
+ size: "sm",
342
+ c: "blue",
343
+ classNames: {
344
+ root: classNames?.copyButtonText?.root
345
+ },
346
+ children: "Copy link"
347
+ }
348
+ )
349
+ }
350
+ )
351
+ ]
352
+ }
353
+ ),
354
+ item.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
355
+ import_core.Text,
356
+ {
357
+ size: "xs",
358
+ c: "red",
359
+ classNames: {
360
+ root: classNames?.itemError?.root
361
+ },
362
+ children: item.error || "Upload failed"
363
+ }
364
+ )
365
+ ]
366
+ }
367
+ )
368
+ }
369
+ ) }, item.id)) })
370
+ ]
371
+ }
372
+ )
373
+ ]
374
+ }
375
+ );
376
+ }
377
+
378
+ // src/components/MediaGrid.tsx
379
+ var import_core2 = require("@mantine/core");
380
+ var import_react2 = require("react");
381
+ var import_io5 = require("react-icons/io5");
382
+
383
+ // src/components/styles/media-grid.module.scss
384
+ var media_grid_module_default = {};
385
+
386
+ // src/components/MediaGrid.tsx
387
+ var import_jsx_runtime2 = require("react/jsx-runtime");
388
+ function MediaGrid({
389
+ images,
390
+ selectedImageId = null,
391
+ onImageClick,
392
+ sortOptions = [
393
+ { value: "createdAt", label: "By created date" },
394
+ { value: "updatedAt", label: "By updated date" },
395
+ { value: "title", label: "By title" }
396
+ ],
397
+ classNames
398
+ }) {
399
+ const [searchQuery, setSearchQuery] = (0, import_react2.useState)("");
400
+ const [sortBy, setSortBy] = (0, import_react2.useState)("createdAt");
401
+ const filteredAndSortedImages = () => {
402
+ let result = [...images];
403
+ if (searchQuery.trim()) {
404
+ const query = searchQuery.toLowerCase();
405
+ result = result.filter(
406
+ (image) => image.title?.toLowerCase().includes(query)
407
+ );
408
+ }
409
+ if (sortBy) {
410
+ result.sort((a, b) => {
411
+ switch (sortBy) {
412
+ case "createdAt":
413
+ return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
414
+ case "updatedAt":
415
+ return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime();
416
+ case "title":
417
+ return (a.title ?? "").localeCompare(b.title ?? "");
418
+ default:
419
+ return 0;
420
+ }
421
+ });
422
+ }
423
+ return result;
424
+ };
425
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
426
+ import_core2.Stack,
427
+ {
428
+ gap: "md",
429
+ classNames: {
430
+ root: classNames?.rootStack?.root
431
+ },
432
+ children: [
433
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
434
+ import_core2.Group,
435
+ {
436
+ justify: "space-between",
437
+ align: "center",
438
+ classNames: {
439
+ root: classNames?.filterGroup?.root
440
+ },
441
+ children: [
442
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
443
+ import_core2.Select,
444
+ {
445
+ placeholder: "Sort by",
446
+ value: sortBy,
447
+ onChange: setSortBy,
448
+ data: sortOptions,
449
+ w: 200,
450
+ clearable: false,
451
+ classNames: classNames?.sortSelect
452
+ }
453
+ ),
454
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
455
+ import_core2.TextInput,
456
+ {
457
+ placeholder: "Search by title...",
458
+ value: searchQuery,
459
+ onChange: (e) => setSearchQuery(e.currentTarget.value),
460
+ leftSection: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_io5.IoSearch, { size: 16 }),
461
+ w: 250,
462
+ classNames: classNames?.searchInput
463
+ }
464
+ )
465
+ ]
466
+ }
467
+ ),
468
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
469
+ import_core2.Grid,
470
+ {
471
+ gutter: "md",
472
+ classNames: classNames?.grid,
473
+ children: filteredAndSortedImages().map((image) => {
474
+ const isSelected = selectedImageId === image.id;
475
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core2.GridCol, { span: { base: 6, sm: 4, md: 3, lg: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
476
+ import_core2.Paper,
477
+ {
478
+ p: "xs",
479
+ withBorder: true,
480
+ radius: "md",
481
+ onClick: () => onImageClick(image.id),
482
+ classNames: {
483
+ root: cx(
484
+ media_grid_module_default.itemPaperRoot,
485
+ classNames?.itemPaper?.root,
486
+ isSelected ? media_grid_module_default.selectedPaper : void 0,
487
+ isSelected ? classNames?.selectedPaper : void 0
488
+ )
489
+ },
490
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
491
+ import_core2.Stack,
492
+ {
493
+ gap: 6,
494
+ classNames: {
495
+ root: classNames?.itemStack?.root
496
+ },
497
+ children: [
498
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: cx(media_grid_module_default.imageContainer, classNames?.imageContainer), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
499
+ import_core2.Image,
500
+ {
501
+ src: image.url,
502
+ alt: image.title || image.name,
503
+ fit: "cover",
504
+ classNames: {
505
+ root: cx(media_grid_module_default.itemImageRoot, classNames?.itemImage?.root)
506
+ }
507
+ }
508
+ ) }),
509
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
510
+ import_core2.Text,
511
+ {
512
+ size: "xs",
513
+ c: "dimmed",
514
+ lineClamp: 1,
515
+ title: image.title ?? image.name,
516
+ classNames: {
517
+ root: cx(media_grid_module_default.itemTextRoot, classNames?.itemText?.root)
518
+ },
519
+ children: image.title ?? image.name
520
+ }
521
+ )
522
+ ]
523
+ }
524
+ )
525
+ }
526
+ ) }, image.id);
527
+ })
528
+ }
529
+ )
530
+ ]
531
+ }
532
+ );
533
+ }
534
+
535
+ // src/components/MediaDetail.tsx
536
+ var import_core3 = require("@mantine/core");
537
+ var import_react3 = require("react");
538
+ var import_md = require("react-icons/md");
539
+ var import_gr = require("react-icons/gr");
540
+ var import_io52 = require("react-icons/io5");
541
+
542
+ // src/components/styles/media-detail.module.scss
543
+ var media_detail_module_default = {};
544
+
545
+ // src/components/MediaDetail.tsx
546
+ var import_jsx_runtime3 = require("react/jsx-runtime");
547
+ function MediaDetail({
548
+ image,
549
+ onUpdate,
550
+ onDelete,
551
+ onCopyLink,
552
+ onNotify,
553
+ classNames
554
+ }) {
555
+ const [title, setTitle] = (0, import_react3.useState)(image.title || "");
556
+ const [description, setDescription] = (0, import_react3.useState)(image.description || "");
557
+ const [originalTitle, setOriginalTitle] = (0, import_react3.useState)(image.title || "");
558
+ const [originalDescription, setOriginalDescription] = (0, import_react3.useState)(image.description || "");
559
+ const isTitleDirty = title !== originalTitle;
560
+ const isDescriptionDirty = description !== originalDescription;
561
+ const [isSavingTitle, setIsSavingTitle] = (0, import_react3.useState)(false);
562
+ const [isSavingDescription, setIsSavingDescription] = (0, import_react3.useState)(false);
563
+ const handleSaveTitle = async () => {
564
+ setIsSavingTitle(true);
565
+ try {
566
+ await onUpdate(image.id, { title: title || null });
567
+ setOriginalTitle(title);
568
+ onNotify?.("success", "Title saved");
569
+ } catch (error) {
570
+ onNotify?.("error", error.message);
571
+ } finally {
572
+ setIsSavingTitle(false);
573
+ }
574
+ };
575
+ const handleSaveDescription = async () => {
576
+ setIsSavingDescription(true);
577
+ try {
578
+ await onUpdate(image.id, { description: description || null });
579
+ setOriginalDescription(description);
580
+ onNotify?.("success", "Description saved");
581
+ } catch (error) {
582
+ onNotify?.("error", error.message);
583
+ } finally {
584
+ setIsSavingDescription(false);
585
+ }
586
+ };
587
+ const handleCopyLink = async () => {
588
+ if (onCopyLink) {
589
+ onCopyLink(image.url);
590
+ } else {
591
+ try {
592
+ await navigator.clipboard.writeText(image.url);
593
+ onNotify?.("success", "Link copied");
594
+ } catch {
595
+ onNotify?.("error", "Failed to copy link");
596
+ }
597
+ }
598
+ };
599
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
600
+ import_core3.Paper,
601
+ {
602
+ p: "md",
603
+ radius: "md",
604
+ withBorder: true,
605
+ classNames: {
606
+ root: cx(media_detail_module_default.paperRoot, classNames?.paper?.root)
607
+ },
608
+ children: [
609
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_core3.Stack, { gap: "md", children: [
610
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
611
+ import_core3.Title,
612
+ {
613
+ order: 4,
614
+ classNames: {
615
+ root: cx(media_detail_module_default.titleRoot, classNames?.title?.root)
616
+ },
617
+ children: "Image Details"
618
+ }
619
+ ),
620
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: cx(media_detail_module_default.imageWrapper, classNames?.imageWrapper), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
621
+ import_core3.Image,
622
+ {
623
+ src: image.url,
624
+ alt: image.title || image.name,
625
+ fit: "contain",
626
+ radius: "md",
627
+ classNames: {
628
+ root: cx(media_detail_module_default.imageRoot, classNames?.image?.root)
629
+ }
630
+ }
631
+ ) }),
632
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
633
+ import_core3.Stack,
634
+ {
635
+ gap: "xs",
636
+ classNames: {
637
+ root: cx(media_detail_module_default.fieldsStackRoot, classNames?.fieldsStack?.root)
638
+ },
639
+ children: [
640
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
641
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
642
+ import_core3.Text,
643
+ {
644
+ size: "sm",
645
+ fw: 500,
646
+ c: "dimmed",
647
+ classNames: {
648
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
649
+ },
650
+ children: "Name"
651
+ }
652
+ ),
653
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
654
+ import_core3.Text,
655
+ {
656
+ classNames: {
657
+ root: cx(media_detail_module_default.fieldValueRoot, classNames?.fieldValue?.root)
658
+ },
659
+ children: image.name
660
+ }
661
+ )
662
+ ] }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
664
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_core3.Group, { gap: 4, align: "center", children: [
665
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
666
+ import_core3.Text,
667
+ {
668
+ size: "sm",
669
+ fw: 500,
670
+ c: "dimmed",
671
+ classNames: {
672
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
673
+ },
674
+ children: "Title"
675
+ }
676
+ ),
677
+ isTitleDirty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
678
+ import_core3.ActionIcon,
679
+ {
680
+ size: "xs",
681
+ variant: "filled",
682
+ color: "green",
683
+ onClick: handleSaveTitle,
684
+ loading: isSavingTitle,
685
+ title: "Save title",
686
+ classNames: {
687
+ root: cx(media_detail_module_default.saveButtonRoot, classNames?.saveButton?.root)
688
+ },
689
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_io52.IoCheckmark, { size: 12 })
690
+ }
691
+ )
692
+ ] }),
693
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
694
+ import_core3.Textarea,
695
+ {
696
+ value: title,
697
+ placeholder: "Enter title",
698
+ onChange: (e) => setTitle(e.target.value),
699
+ minRows: 2,
700
+ autosize: true,
701
+ classNames: {
702
+ root: cx(media_detail_module_default.textareaRoot, classNames?.textarea?.root),
703
+ input: cx(media_detail_module_default.textareaInput, classNames?.textarea?.input),
704
+ wrapper: cx(media_detail_module_default.textareaWrapper, classNames?.textarea?.wrapper)
705
+ }
706
+ }
707
+ )
708
+ ] }),
709
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_core3.Group, { gap: 4, align: "center", children: [
711
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
712
+ import_core3.Text,
713
+ {
714
+ size: "sm",
715
+ fw: 500,
716
+ c: "dimmed",
717
+ classNames: {
718
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
719
+ },
720
+ children: "Description"
721
+ }
722
+ ),
723
+ isDescriptionDirty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
724
+ import_core3.ActionIcon,
725
+ {
726
+ size: "xs",
727
+ variant: "filled",
728
+ color: "green",
729
+ onClick: handleSaveDescription,
730
+ loading: isSavingDescription,
731
+ title: "Save description",
732
+ classNames: {
733
+ root: cx(media_detail_module_default.saveButtonRoot, classNames?.saveButton?.root)
734
+ },
735
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_io52.IoCheckmark, { size: 12 })
736
+ }
737
+ )
738
+ ] }),
739
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
740
+ import_core3.Textarea,
741
+ {
742
+ value: description,
743
+ placeholder: "Enter description",
744
+ onChange: (e) => setDescription(e.target.value),
745
+ minRows: 3,
746
+ autosize: true,
747
+ classNames: {
748
+ root: cx(media_detail_module_default.textareaRoot, classNames?.textarea?.root),
749
+ input: cx(media_detail_module_default.textareaInput, classNames?.textarea?.input),
750
+ wrapper: cx(media_detail_module_default.textareaWrapper, classNames?.textarea?.wrapper)
751
+ }
752
+ }
753
+ )
754
+ ] }),
755
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
756
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
757
+ import_core3.Text,
758
+ {
759
+ size: "sm",
760
+ fw: 500,
761
+ c: "dimmed",
762
+ classNames: {
763
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
764
+ },
765
+ children: "Type"
766
+ }
767
+ ),
768
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
769
+ import_core3.Text,
770
+ {
771
+ classNames: {
772
+ root: cx(media_detail_module_default.fieldValueRoot, classNames?.fieldValue?.root)
773
+ },
774
+ children: image.type
775
+ }
776
+ )
777
+ ] }),
778
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
780
+ import_core3.Text,
781
+ {
782
+ size: "sm",
783
+ fw: 500,
784
+ c: "dimmed",
785
+ classNames: {
786
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
787
+ },
788
+ children: "URL"
789
+ }
790
+ ),
791
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_core3.Group, { gap: "xs", align: "center", wrap: "nowrap", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
793
+ import_core3.Text,
794
+ {
795
+ size: "xs",
796
+ c: "dimmed",
797
+ classNames: {
798
+ root: cx(media_detail_module_default.urlText, classNames?.urlText)
799
+ },
800
+ children: image.url
801
+ }
802
+ ),
803
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
804
+ import_core3.ActionIcon,
805
+ {
806
+ variant: "subtle",
807
+ color: "gray",
808
+ onClick: handleCopyLink,
809
+ size: "sm",
810
+ classNames: {
811
+ root: cx(media_detail_module_default.copyButtonRoot, classNames?.copyButton?.root)
812
+ },
813
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_md.MdOutlineFileCopy, { size: 18 })
814
+ }
815
+ )
816
+ ] })
817
+ ] }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
819
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
820
+ import_core3.Text,
821
+ {
822
+ size: "sm",
823
+ fw: 500,
824
+ c: "dimmed",
825
+ classNames: {
826
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
827
+ },
828
+ children: "Created At"
829
+ }
830
+ ),
831
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
832
+ import_core3.Text,
833
+ {
834
+ size: "sm",
835
+ classNames: {
836
+ root: cx(media_detail_module_default.fieldValueRoot, classNames?.fieldValue?.root)
837
+ },
838
+ children: new Date(image.createdAt).toLocaleString()
839
+ }
840
+ )
841
+ ] }),
842
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cx(media_detail_module_default.fieldGroup, classNames?.fieldGroup), children: [
843
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
844
+ import_core3.Text,
845
+ {
846
+ size: "sm",
847
+ fw: 500,
848
+ c: "dimmed",
849
+ classNames: {
850
+ root: cx(media_detail_module_default.fieldLabelRoot, classNames?.fieldLabel?.root)
851
+ },
852
+ children: "Updated At"
853
+ }
854
+ ),
855
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
856
+ import_core3.Text,
857
+ {
858
+ size: "sm",
859
+ classNames: {
860
+ root: cx(media_detail_module_default.fieldValueRoot, classNames?.fieldValue?.root)
861
+ },
862
+ children: new Date(image.updatedAt).toLocaleString()
863
+ }
864
+ )
865
+ ] })
866
+ ]
867
+ }
868
+ )
869
+ ] }),
870
+ onDelete && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
871
+ import_core3.ActionIcon,
872
+ {
873
+ variant: "filled",
874
+ color: "red",
875
+ size: "lg",
876
+ onClick: () => onDelete(image.id),
877
+ classNames: {
878
+ root: cx(media_detail_module_default.deleteButtonRoot, classNames?.deleteButton?.root)
879
+ },
880
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_gr.GrTrash, { size: 20 })
881
+ }
882
+ )
883
+ ]
884
+ }
885
+ ) });
886
+ }
887
+ // Annotate the CommonJS export names for ESM import in node:
888
+ 0 && (module.exports = {
889
+ MediaDetail,
890
+ MediaGrid,
891
+ MediaUpload,
892
+ cx,
893
+ formatFileSize,
894
+ validateImageFile
895
+ });
896
+ //# sourceMappingURL=index.js.map