@commercetools-demo/puck-image-picker 0.2.2

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/dist/index.js ADDED
@@ -0,0 +1,748 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ImagePickerField: () => ImagePickerField,
34
+ ImagePickerProvider: () => ImagePickerProvider,
35
+ useMediaLibrary: () => useMediaLibrary
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/ImagePickerField.tsx
40
+ var import_react3 = require("react");
41
+
42
+ // src/hooks/useMediaLibrary.ts
43
+ var import_react2 = require("react");
44
+
45
+ // src/api/media-library.api.ts
46
+ var fetchMediaLibraryApi = async (baseURL, projectKey, businessUnitKey, jwtToken, extensions = [], page = 1, limit = 20) => {
47
+ const params = new URLSearchParams();
48
+ if (extensions.length > 0) params.set("extensions", extensions.join(","));
49
+ params.set("page", String(page));
50
+ params.set("limit", String(limit));
51
+ const headers = {
52
+ "x-project-key": projectKey
53
+ };
54
+ if (jwtToken) headers["Authorization"] = `Bearer ${jwtToken}`;
55
+ const res = await fetch(
56
+ `${baseURL}/${businessUnitKey}/media-library?${params}`,
57
+ { headers }
58
+ );
59
+ if (!res.ok) {
60
+ const body = await res.text();
61
+ throw new Error(`[puck-image-picker] HTTP ${res.status}: ${body || res.statusText}`);
62
+ }
63
+ return res.json();
64
+ };
65
+ var uploadMediaFileApi = async (baseURL, projectKey, businessUnitKey, jwtToken, file, title, description) => {
66
+ const formData = new FormData();
67
+ formData.append("file", file);
68
+ if (title) formData.append("title", title);
69
+ if (description) formData.append("description", description);
70
+ const res = await fetch(
71
+ `${baseURL}/${businessUnitKey}/upload-file`,
72
+ {
73
+ method: "POST",
74
+ headers: {
75
+ "x-project-key": projectKey,
76
+ Authorization: `Bearer ${jwtToken}`
77
+ },
78
+ body: formData
79
+ }
80
+ );
81
+ if (!res.ok) {
82
+ const body = await res.text();
83
+ throw new Error(`[puck-image-picker] HTTP ${res.status}: ${body || res.statusText}`);
84
+ }
85
+ return res.json();
86
+ };
87
+
88
+ // src/context/ImagePickerContext.tsx
89
+ var import_react = require("react");
90
+ var ImagePickerContext = (0, import_react.createContext)(null);
91
+ var useImagePickerContext = () => {
92
+ const ctx = (0, import_react.useContext)(ImagePickerContext);
93
+ if (!ctx) {
94
+ throw new Error(
95
+ "useImagePickerContext must be used inside <ImagePickerProvider>. Wrap your component tree with <ImagePickerProvider>."
96
+ );
97
+ }
98
+ return ctx;
99
+ };
100
+
101
+ // src/hooks/useMediaLibrary.ts
102
+ var initialPagination = {
103
+ totalItems: 0,
104
+ totalPages: 0,
105
+ currentPage: 1,
106
+ limit: 20
107
+ };
108
+ var useMediaLibrary = () => {
109
+ const { baseURL, projectKey, businessUnitKey, jwtToken } = useImagePickerContext();
110
+ const [state, setState] = (0, import_react2.useState)({
111
+ files: [],
112
+ pagination: initialPagination,
113
+ loading: false,
114
+ uploading: false,
115
+ error: null
116
+ });
117
+ const fetchMedia = (0, import_react2.useCallback)(
118
+ async (extensions = [], page = 1, limit = 20) => {
119
+ setState((s) => ({ ...s, loading: true, error: null }));
120
+ try {
121
+ const result = await fetchMediaLibraryApi(
122
+ baseURL,
123
+ projectKey,
124
+ businessUnitKey,
125
+ jwtToken,
126
+ extensions,
127
+ page,
128
+ limit
129
+ );
130
+ setState((s) => ({
131
+ ...s,
132
+ files: result.files,
133
+ pagination: result.pagination,
134
+ loading: false
135
+ }));
136
+ } catch (err) {
137
+ setState((s) => ({
138
+ ...s,
139
+ loading: false,
140
+ error: err.message
141
+ }));
142
+ }
143
+ },
144
+ [baseURL, projectKey, businessUnitKey, jwtToken]
145
+ );
146
+ const uploadFile = (0, import_react2.useCallback)(
147
+ async (file, title, description) => {
148
+ if (!jwtToken) throw new Error("jwtToken is required to upload files");
149
+ setState((s) => ({ ...s, uploading: true, error: null }));
150
+ try {
151
+ const result = await uploadMediaFileApi(
152
+ baseURL,
153
+ projectKey,
154
+ businessUnitKey,
155
+ jwtToken,
156
+ file,
157
+ title,
158
+ description
159
+ );
160
+ const mediaFile = {
161
+ url: result.url,
162
+ name: file.name,
163
+ title: title ?? file.name,
164
+ description,
165
+ isImage: file.type.startsWith("image/"),
166
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
167
+ size: file.size
168
+ };
169
+ setState((s) => ({
170
+ ...s,
171
+ files: [mediaFile, ...s.files],
172
+ pagination: {
173
+ ...s.pagination,
174
+ totalItems: s.pagination.totalItems + 1
175
+ },
176
+ uploading: false
177
+ }));
178
+ return mediaFile;
179
+ } catch (err) {
180
+ setState((s) => ({
181
+ ...s,
182
+ uploading: false,
183
+ error: err.message
184
+ }));
185
+ throw err;
186
+ }
187
+ },
188
+ [baseURL, projectKey, businessUnitKey, jwtToken]
189
+ );
190
+ const loadNextPage = (0, import_react2.useCallback)(
191
+ async (extensions = []) => {
192
+ const next = state.pagination.currentPage + 1;
193
+ if (next <= state.pagination.totalPages) {
194
+ await fetchMedia(extensions, next, state.pagination.limit);
195
+ }
196
+ },
197
+ [fetchMedia, state.pagination]
198
+ );
199
+ const loadPreviousPage = (0, import_react2.useCallback)(
200
+ async (extensions = []) => {
201
+ const prev = state.pagination.currentPage - 1;
202
+ if (prev >= 1) {
203
+ await fetchMedia(extensions, prev, state.pagination.limit);
204
+ }
205
+ },
206
+ [fetchMedia, state.pagination]
207
+ );
208
+ const hasNextPage = (0, import_react2.useCallback)(
209
+ () => state.pagination.currentPage < state.pagination.totalPages,
210
+ [state.pagination]
211
+ );
212
+ const hasPreviousPage = (0, import_react2.useCallback)(
213
+ () => state.pagination.currentPage > 1,
214
+ [state.pagination]
215
+ );
216
+ return {
217
+ files: state.files,
218
+ pagination: state.pagination,
219
+ loading: state.loading,
220
+ uploading: state.uploading,
221
+ error: state.error,
222
+ fetchMedia,
223
+ uploadFile,
224
+ loadNextPage,
225
+ loadPreviousPage,
226
+ hasNextPage,
227
+ hasPreviousPage
228
+ };
229
+ };
230
+
231
+ // src/ImagePickerField.tsx
232
+ var import_primary_button = __toESM(require("@commercetools-uikit/primary-button"));
233
+ var import_secondary_button = __toESM(require("@commercetools-uikit/secondary-button"));
234
+ var import_flat_button = __toESM(require("@commercetools-uikit/flat-button"));
235
+ var import_secondary_icon_button = __toESM(require("@commercetools-uikit/secondary-icon-button"));
236
+ var import_text_input = __toESM(require("@commercetools-uikit/text-input"));
237
+ var import_label = __toESM(require("@commercetools-uikit/label"));
238
+ var import_loading_spinner = __toESM(require("@commercetools-uikit/loading-spinner"));
239
+ var import_spacings = __toESM(require("@commercetools-uikit/spacings"));
240
+ var import_text = __toESM(require("@commercetools-uikit/text"));
241
+ var import_icons = require("@commercetools-uikit/icons");
242
+ var import_jsx_runtime = require("react/jsx-runtime");
243
+ var UploadModal = ({
244
+ uploading,
245
+ error,
246
+ imagesOnly,
247
+ onUpload,
248
+ onClose
249
+ }) => {
250
+ const [file, setFile] = (0, import_react3.useState)(null);
251
+ const [title, setTitle] = (0, import_react3.useState)("");
252
+ const [description, setDescription] = (0, import_react3.useState)("");
253
+ const [dragging, setDragging] = (0, import_react3.useState)(false);
254
+ const inputRef = (0, import_react3.useRef)(null);
255
+ const handleFile = (f) => {
256
+ setFile(f);
257
+ if (!title) setTitle(f.name);
258
+ };
259
+ const handleDrop = (e) => {
260
+ e.preventDefault();
261
+ setDragging(false);
262
+ const f = e.dataTransfer.files[0];
263
+ if (f) handleFile(f);
264
+ };
265
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
266
+ "div",
267
+ {
268
+ style: {
269
+ position: "fixed",
270
+ inset: 0,
271
+ background: "rgba(0,0,0,0.5)",
272
+ zIndex: 9999,
273
+ display: "flex",
274
+ alignItems: "center",
275
+ justifyContent: "center"
276
+ },
277
+ onClick: (e) => e.target === e.currentTarget && onClose(),
278
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
279
+ "div",
280
+ {
281
+ style: {
282
+ background: "#fff",
283
+ borderRadius: "8px",
284
+ width: "640px",
285
+ maxWidth: "95vw",
286
+ maxHeight: "85vh",
287
+ display: "flex",
288
+ flexDirection: "column",
289
+ boxShadow: "0 20px 60px rgba(0,0,0,0.3)"
290
+ },
291
+ children: [
292
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
293
+ "div",
294
+ {
295
+ style: {
296
+ display: "flex",
297
+ alignItems: "center",
298
+ justifyContent: "space-between",
299
+ padding: "16px 20px",
300
+ borderBottom: "1px solid #e5e7eb"
301
+ },
302
+ children: [
303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Subheadline, { as: "h4", isBold: true, children: "Upload a file" }),
304
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
305
+ import_secondary_icon_button.default,
306
+ {
307
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.CloseIcon, {}),
308
+ label: "Close",
309
+ onClick: onClose,
310
+ size: "small"
311
+ }
312
+ )
313
+ ]
314
+ }
315
+ ),
316
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, overflow: "auto", padding: "20px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "m", children: [
317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "xs", children: [
318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.default, { htmlFor: "upload-title", children: "Title" }),
319
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
320
+ import_text_input.default,
321
+ {
322
+ id: "upload-title",
323
+ value: title,
324
+ onChange: (e) => setTitle(e.target.value),
325
+ placeholder: "File title"
326
+ }
327
+ )
328
+ ] }),
329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "xs", children: [
330
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_label.default, { htmlFor: "upload-description", children: "Description" }),
331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
332
+ import_text_input.default,
333
+ {
334
+ id: "upload-description",
335
+ value: description,
336
+ onChange: (e) => setDescription(e.target.value),
337
+ placeholder: "Optional description"
338
+ }
339
+ )
340
+ ] }),
341
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
342
+ "div",
343
+ {
344
+ style: {
345
+ border: dragging ? "2px dashed #3b82f6" : "2px dashed #d1d5db",
346
+ borderRadius: "6px",
347
+ padding: "24px",
348
+ textAlign: "center",
349
+ cursor: "pointer",
350
+ color: dragging ? "#3b82f6" : "#6b7280",
351
+ fontSize: "13px",
352
+ transition: "border-color .2s"
353
+ },
354
+ onClick: () => inputRef.current?.click(),
355
+ onDragOver: (e) => {
356
+ e.preventDefault();
357
+ setDragging(true);
358
+ },
359
+ onDragLeave: () => setDragging(false),
360
+ onDrop: handleDrop,
361
+ children: [
362
+ file ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
363
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
364
+ "\u{1F4CE} ",
365
+ file.name
366
+ ] }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: "11px", marginTop: "4px", color: "#9ca3af" }, children: [
368
+ (file.size / 1024).toFixed(0),
369
+ " KB"
370
+ ] })
371
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "\u{1F4C1} Click or drag & drop to select a file" }),
372
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
373
+ "input",
374
+ {
375
+ ref: inputRef,
376
+ type: "file",
377
+ accept: imagesOnly ? "image/*" : "*/*",
378
+ style: { display: "none" },
379
+ onChange: (e) => {
380
+ const f = e.target.files?.[0];
381
+ if (f) handleFile(f);
382
+ }
383
+ }
384
+ )
385
+ ]
386
+ }
387
+ ),
388
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "negative", children: error })
389
+ ] }) }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
391
+ "div",
392
+ {
393
+ style: {
394
+ display: "flex",
395
+ justifyContent: "space-between",
396
+ alignItems: "center",
397
+ padding: "12px 20px",
398
+ borderTop: "1px solid #e5e7eb",
399
+ gap: "12px"
400
+ },
401
+ children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Detail, { tone: "secondary", truncate: true, children: file?.name ?? "No file selected" }),
403
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { scale: "s", children: [
404
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_secondary_button.default, { label: "Cancel", onClick: onClose }),
405
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
406
+ import_primary_button.default,
407
+ {
408
+ label: uploading ? "Uploading\u2026" : "Upload",
409
+ isDisabled: !file || uploading,
410
+ onClick: () => file && onUpload(file, title, description)
411
+ }
412
+ )
413
+ ] })
414
+ ]
415
+ }
416
+ )
417
+ ]
418
+ }
419
+ )
420
+ }
421
+ );
422
+ };
423
+ var LibraryModal = ({
424
+ files,
425
+ pagination,
426
+ loading,
427
+ error,
428
+ onNextPage,
429
+ onPrevPage,
430
+ onSelect,
431
+ onClose
432
+ }) => {
433
+ const [selected, setSelected] = (0, import_react3.useState)(null);
434
+ const handleConfirm = () => {
435
+ if (selected) onSelect(selected);
436
+ };
437
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
438
+ "div",
439
+ {
440
+ style: {
441
+ position: "fixed",
442
+ inset: 0,
443
+ background: "rgba(0,0,0,0.5)",
444
+ zIndex: 9999,
445
+ display: "flex",
446
+ alignItems: "center",
447
+ justifyContent: "center"
448
+ },
449
+ onClick: (e) => e.target === e.currentTarget && onClose(),
450
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
451
+ "div",
452
+ {
453
+ style: {
454
+ background: "#fff",
455
+ borderRadius: "8px",
456
+ width: "640px",
457
+ maxWidth: "95vw",
458
+ maxHeight: "85vh",
459
+ display: "flex",
460
+ flexDirection: "column",
461
+ boxShadow: "0 20px 60px rgba(0,0,0,0.3)"
462
+ },
463
+ children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
465
+ "div",
466
+ {
467
+ style: {
468
+ display: "flex",
469
+ alignItems: "center",
470
+ justifyContent: "space-between",
471
+ padding: "16px 20px",
472
+ borderBottom: "1px solid #e5e7eb"
473
+ },
474
+ children: [
475
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Subheadline, { as: "h4", isBold: true, children: "Select from Media Library" }),
476
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
477
+ import_secondary_icon_button.default,
478
+ {
479
+ icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.CloseIcon, {}),
480
+ label: "Close",
481
+ onClick: onClose,
482
+ size: "small"
483
+ }
484
+ )
485
+ ]
486
+ }
487
+ ),
488
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { flex: 1, overflow: "auto", padding: "20px" }, children: [
489
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "32px" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_loading_spinner.default, {}) }) : files.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "secondary", children: "No files found." }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "m", children: [
490
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
491
+ "div",
492
+ {
493
+ style: {
494
+ display: "grid",
495
+ gridTemplateColumns: "repeat(auto-fill, minmax(110px, 1fr))",
496
+ gap: "12px"
497
+ },
498
+ children: files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
499
+ "div",
500
+ {
501
+ style: {
502
+ display: "flex",
503
+ flexDirection: "column",
504
+ alignItems: "center",
505
+ cursor: "pointer",
506
+ border: `2px solid ${selected?.url === file.url ? "#3b82f6" : "transparent"}`,
507
+ borderRadius: "6px",
508
+ padding: "6px",
509
+ background: selected?.url === file.url ? "rgba(59,130,246,0.07)" : "#f9fafb"
510
+ },
511
+ onClick: () => setSelected(file),
512
+ title: file.title ?? file.name,
513
+ children: [
514
+ file.isImage ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
515
+ "img",
516
+ {
517
+ src: file.url,
518
+ alt: file.name,
519
+ style: { width: "80px", height: "80px", objectFit: "cover", borderRadius: "4px", background: "#e5e7eb" }
520
+ }
521
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
522
+ "div",
523
+ {
524
+ style: {
525
+ width: "80px",
526
+ height: "80px",
527
+ borderRadius: "4px",
528
+ background: "#e5e7eb",
529
+ display: "flex",
530
+ alignItems: "center",
531
+ justifyContent: "center",
532
+ fontSize: "28px"
533
+ },
534
+ children: "\u{1F4C4}"
535
+ }
536
+ ),
537
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
538
+ "div",
539
+ {
540
+ style: {
541
+ marginTop: "6px",
542
+ fontSize: "11px",
543
+ textAlign: "center",
544
+ maxWidth: "100%",
545
+ overflow: "hidden",
546
+ textOverflow: "ellipsis",
547
+ whiteSpace: "nowrap",
548
+ color: "#374151"
549
+ },
550
+ children: file.title ?? file.name
551
+ }
552
+ )
553
+ ]
554
+ },
555
+ file.url
556
+ ))
557
+ }
558
+ ),
559
+ pagination.totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { alignItems: "center", justifyContent: "center", scale: "s", children: [
560
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
561
+ import_secondary_button.default,
562
+ {
563
+ label: "\u2190 Prev",
564
+ isDisabled: pagination.currentPage <= 1,
565
+ onClick: onPrevPage,
566
+ size: "small"
567
+ }
568
+ ),
569
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_text.default.Detail, { tone: "secondary", children: [
570
+ pagination.currentPage,
571
+ " / ",
572
+ pagination.totalPages
573
+ ] }),
574
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
575
+ import_secondary_button.default,
576
+ {
577
+ label: "Next \u2192",
578
+ isDisabled: pagination.currentPage >= pagination.totalPages,
579
+ onClick: onNextPage,
580
+ size: "small"
581
+ }
582
+ )
583
+ ] })
584
+ ] }),
585
+ error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Body, { tone: "negative", children: error })
586
+ ] }),
587
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
588
+ "div",
589
+ {
590
+ style: {
591
+ display: "flex",
592
+ justifyContent: "space-between",
593
+ alignItems: "center",
594
+ padding: "12px 20px",
595
+ borderTop: "1px solid #e5e7eb",
596
+ gap: "12px"
597
+ },
598
+ children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Detail, { tone: "secondary", truncate: true, children: selected ? selected.title ?? selected.name : "Nothing selected" }),
600
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { scale: "s", children: [
601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_secondary_button.default, { label: "Cancel", onClick: onClose }),
602
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
603
+ import_primary_button.default,
604
+ {
605
+ label: "Select",
606
+ isDisabled: !selected,
607
+ onClick: handleConfirm
608
+ }
609
+ )
610
+ ] })
611
+ ]
612
+ }
613
+ )
614
+ ]
615
+ }
616
+ )
617
+ }
618
+ );
619
+ };
620
+ var ImagePickerField = ({
621
+ value,
622
+ onChange,
623
+ imagesOnly = true
624
+ }) => {
625
+ const {
626
+ files,
627
+ pagination,
628
+ loading,
629
+ uploading,
630
+ error,
631
+ fetchMedia,
632
+ uploadFile,
633
+ loadNextPage,
634
+ loadPreviousPage
635
+ } = useMediaLibrary();
636
+ const [showUpload, setShowUpload] = (0, import_react3.useState)(false);
637
+ const [showLibrary, setShowLibrary] = (0, import_react3.useState)(false);
638
+ const extensions = imagesOnly ? ["jpg", "jpeg", "png", "gif", "webp", "svg"] : [];
639
+ const openLibrary = (0, import_react3.useCallback)(() => {
640
+ void fetchMedia(extensions, 1, 20);
641
+ setShowLibrary(true);
642
+ }, [fetchMedia, extensions]);
643
+ const handleUpload = (0, import_react3.useCallback)(
644
+ async (file, title, description) => {
645
+ const mediaFile = await uploadFile(file, title, description);
646
+ onChange(mediaFile.url);
647
+ setShowUpload(false);
648
+ },
649
+ [uploadFile, onChange]
650
+ );
651
+ const handleSelect = (0, import_react3.useCallback)(
652
+ (file) => {
653
+ onChange(file.url);
654
+ setShowLibrary(false);
655
+ },
656
+ [onChange]
657
+ );
658
+ (0, import_react3.useEffect)(() => {
659
+ const onKey = (e) => {
660
+ if (e.key === "Escape") {
661
+ setShowUpload(false);
662
+ setShowLibrary(false);
663
+ }
664
+ };
665
+ window.addEventListener("keydown", onKey);
666
+ return () => window.removeEventListener("keydown", onKey);
667
+ }, []);
668
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Stack, { scale: "s", children: [
669
+ value ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
670
+ "div",
671
+ {
672
+ style: {
673
+ display: "flex",
674
+ alignItems: "center",
675
+ gap: "10px",
676
+ padding: "8px",
677
+ border: "1px solid #d1d5db",
678
+ borderRadius: "6px",
679
+ background: "#f9fafb"
680
+ },
681
+ children: [
682
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
683
+ "img",
684
+ {
685
+ src: value,
686
+ alt: "",
687
+ style: { width: "48px", height: "48px", objectFit: "cover", borderRadius: "4px", flexShrink: 0 }
688
+ }
689
+ ),
690
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { flex: 1, minWidth: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Detail, { tone: "secondary", truncate: true, children: value }) }),
691
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_flat_button.default, { tone: "critical", label: "Remove", onClick: () => onChange("") })
692
+ ]
693
+ }
694
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_text.default.Detail, { tone: "secondary", children: "No image selected" }),
695
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_spacings.default.Inline, { scale: "s", children: [
696
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_secondary_button.default, { label: "Upload", onClick: () => setShowUpload(true) }),
697
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_primary_button.default, { label: "Media Library", onClick: openLibrary })
698
+ ] }),
699
+ showUpload && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
700
+ UploadModal,
701
+ {
702
+ uploading,
703
+ error,
704
+ imagesOnly,
705
+ onUpload: (file, title, desc) => void handleUpload(file, title, desc),
706
+ onClose: () => setShowUpload(false)
707
+ }
708
+ ),
709
+ showLibrary && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
710
+ LibraryModal,
711
+ {
712
+ files,
713
+ pagination,
714
+ loading,
715
+ error,
716
+ onNextPage: () => void loadNextPage(extensions),
717
+ onPrevPage: () => void loadPreviousPage(extensions),
718
+ onSelect: handleSelect,
719
+ onClose: () => setShowLibrary(false)
720
+ }
721
+ )
722
+ ] });
723
+ };
724
+
725
+ // src/context/ImagePickerProvider.tsx
726
+ var import_jsx_runtime2 = require("react/jsx-runtime");
727
+ var ImagePickerProvider = ({
728
+ children,
729
+ baseURL,
730
+ projectKey,
731
+ businessUnitKey,
732
+ jwtToken
733
+ }) => {
734
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
735
+ ImagePickerContext.Provider,
736
+ {
737
+ value: { baseURL, projectKey, businessUnitKey, jwtToken },
738
+ children
739
+ }
740
+ );
741
+ };
742
+ // Annotate the CommonJS export names for ESM import in node:
743
+ 0 && (module.exports = {
744
+ ImagePickerField,
745
+ ImagePickerProvider,
746
+ useMediaLibrary
747
+ });
748
+ //# sourceMappingURL=index.js.map