@cropvue/vue 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.
@@ -0,0 +1,831 @@
1
+ import { defineComponent as V, ref as D, computed as L, onMounted as ee, onUnmounted as te, watch as Z, nextTick as ue, openBlock as w, createElementBlock as C, createElementVNode as n, normalizeClass as O, renderSlot as x, normalizeStyle as Y, createCommentVNode as I, createStaticVNode as oe, Fragment as G, unref as i, createVNode as J, toDisplayString as ce, renderList as pe, withModifiers as de } from "vue";
2
+ import { usePointerHandler as ve, isPointInsideStencil as me, handleKeyboard as fe, handleCropMove as he, handleCropResize as ge, handleZoom as ye, handlePan as _e, useCropper as xe, useDropzone as ne, renderCrop as $e, useImageQueue as be } from "@cropvue/core";
3
+ import { useCompressor as Ke, useCropper as Ge, useDropzone as Je, useImageQueue as et, useUploader as tt } from "@cropvue/core";
4
+ const we = ["src"], Ce = {
5
+ key: 0,
6
+ class: "cropvue-editor__handle cropvue-editor__handle--ne",
7
+ "data-handle": "ne"
8
+ }, ke = 14, ze = /* @__PURE__ */ V({
9
+ __name: "CropEditor",
10
+ props: {
11
+ image: {},
12
+ transform: {},
13
+ crop: {},
14
+ pannable: { type: Boolean, default: !0 }
15
+ },
16
+ emits: ["update:transform", "update:crop"],
17
+ setup(f, { expose: k, emit: v }) {
18
+ const e = f, o = v, l = D(), t = D(), s = D(0), p = D(0), m = D(!1), u = L(() => {
19
+ const r = e.image;
20
+ if (!r || s.value === 0 || p.value === 0 || r.naturalWidth === 0 || r.naturalHeight === 0) return 1;
21
+ const a = s.value / r.naturalWidth, h = p.value / r.naturalHeight;
22
+ return Math.min(a, h, 1);
23
+ }), c = L(() => {
24
+ const r = e.image;
25
+ return r ? r.naturalWidth * u.value : 0;
26
+ }), N = L(() => {
27
+ const r = e.image;
28
+ return r ? r.naturalHeight * u.value : 0;
29
+ }), B = L(() => ({
30
+ x: (s.value - c.value) / 2,
31
+ y: (p.value - N.value) / 2
32
+ })), X = L(() => {
33
+ const r = e.transform, a = u.value, h = [];
34
+ return h.push(`translate(${r.x * a}px, ${r.y * a}px)`), h.push(`scale(${r.flipX ? -r.scale : r.scale}, ${r.flipY ? -r.scale : r.scale})`), h.push(`rotate(${r.rotation}deg)`), {
35
+ width: `${c.value}px`,
36
+ height: `${N.value}px`,
37
+ transform: h.join(" "),
38
+ transformOrigin: "center center"
39
+ };
40
+ }), q = L(() => {
41
+ const r = u.value, a = B.value;
42
+ return {
43
+ left: `${e.crop.x * r + a.x}px`,
44
+ top: `${e.crop.y * r + a.y}px`,
45
+ width: `${e.crop.width * r}px`,
46
+ height: `${e.crop.height * r}px`
47
+ };
48
+ }), A = L(() => {
49
+ const r = e.crop, a = u.value, h = B.value, g = r.x * a + h.x, y = r.y * a + h.y, b = r.width * a, $ = r.height * a, W = s.value, P = p.value;
50
+ let S;
51
+ if (r.stencil === "circle") {
52
+ const M = Math.min(b, $) / 2, z = g + b / 2, R = y + $ / 2;
53
+ S = `M${z - M},${R} A${M},${M} 0 1,1 ${z + M},${R} A${M},${M} 0 1,1 ${z - M},${R} Z`;
54
+ } else if (r.stencil === "freeform" && r.points && r.points.length >= 3) {
55
+ const z = r.points.map((R) => `${R.x * a + h.x},${R.y * a + h.y}`);
56
+ S = `M${z[0]} ${z.slice(1).map((R) => `L${R}`).join(" ")} Z`;
57
+ } else
58
+ S = `M${g},${y} L${g + b},${y} L${g + b},${y + $} L${g},${y + $} Z`;
59
+ return { clipPath: `path(evenodd, "${`M0,0 L${W},0 L${W},${P} L0,${P} Z`} ${S}")` };
60
+ }), j = L(() => {
61
+ const r = e.crop, a = u.value, h = B.value, g = r.x * a + h.x, y = r.y * a + h.y, b = r.width * a, $ = r.height * a;
62
+ if (r.stencil === "circle") {
63
+ const U = Math.min(b, $) / 2, M = g + b / 2, z = y + $ / 2;
64
+ return `circle(${U}px at ${M}px ${z}px)`;
65
+ }
66
+ const W = y, P = s.value - (g + b), S = p.value - (y + $);
67
+ return `inset(${W}px ${P}px ${S}px ${g}px)`;
68
+ });
69
+ function d() {
70
+ t.value && (s.value = t.value.clientWidth, p.value = t.value.clientHeight);
71
+ }
72
+ let _ = null;
73
+ function H() {
74
+ _ && (_.destroy(), _ = null);
75
+ const r = l.value;
76
+ r && (_ = ve(r, {
77
+ onPan(a, h) {
78
+ e.pannable && (m.value = !0, o("update:transform", _e(e.transform, a, h)));
79
+ },
80
+ onZoom(a, h, g) {
81
+ e.pannable && o("update:transform", ye(e.transform, a, h, g));
82
+ },
83
+ onCropResize(a, h, g) {
84
+ const y = e.image, b = y ? { width: y.naturalWidth, height: y.naturalHeight } : { width: e.crop.width, height: e.crop.height };
85
+ o("update:crop", ge(e.crop, a, h, g, b));
86
+ },
87
+ onCropMove(a, h) {
88
+ const g = e.image, y = g ? { width: g.naturalWidth, height: g.naturalHeight } : { width: e.crop.width, height: e.crop.height };
89
+ o("update:crop", he(e.crop, a, h, y));
90
+ },
91
+ onKeyboard(a, h) {
92
+ e.pannable && o("update:transform", fe(e.transform, a, h));
93
+ },
94
+ getHandleAtPoint(a) {
95
+ const g = a.target?.getAttribute?.("data-handle");
96
+ if (g) return g;
97
+ const y = u.value, b = B.value, $ = e.crop, W = $.x * y + b.x, P = $.y * y + b.y, S = W + $.width * y, E = P + $.height * y, U = l.value;
98
+ if (!U) return null;
99
+ const M = U.getBoundingClientRect(), z = a.clientX - M.left, R = a.clientY - M.top, F = ke;
100
+ if ($.stencil === "circle") {
101
+ const K = $.width * y / 2, ie = W + K, ae = P + K, le = ie + K * Math.SQRT1_2, se = ae - K * Math.SQRT1_2;
102
+ return Math.abs(z - le) < F && Math.abs(R - se) < F ? "ne" : null;
103
+ }
104
+ return Math.abs(z - W) < F && Math.abs(R - P) < F ? "nw" : Math.abs(z - S) < F && Math.abs(R - P) < F ? "ne" : Math.abs(z - W) < F && Math.abs(R - E) < F ? "sw" : Math.abs(z - S) < F && Math.abs(R - E) < F ? "se" : null;
105
+ },
106
+ isInsideCropArea(a) {
107
+ const h = u.value, g = B.value, y = e.crop, b = l.value;
108
+ if (!b) return !1;
109
+ const $ = b.getBoundingClientRect(), W = a.clientX - $.left, P = a.clientY - $.top, S = (W - g.x) / h, E = (P - g.y) / h;
110
+ return me(S, E, y);
111
+ },
112
+ displayScale: () => u.value
113
+ }));
114
+ }
115
+ let T = null, Q = null;
116
+ ee(() => {
117
+ d(), H(), t.value && (T = new ResizeObserver(() => {
118
+ Q || (Q = requestAnimationFrame(() => {
119
+ d(), Q = null;
120
+ }));
121
+ }), T.observe(t.value));
122
+ }), te(() => {
123
+ T?.disconnect(), T = null, Q && (cancelAnimationFrame(Q), Q = null), _ && (_.destroy(), _ = null);
124
+ }), Z(() => e.image, async () => {
125
+ await ue(), d();
126
+ });
127
+ function re() {
128
+ m.value = !1;
129
+ }
130
+ return k({ editorRef: l, displayScale: u }), (r, a) => (w(), C("div", {
131
+ ref_key: "containerRef",
132
+ ref: t,
133
+ class: "cropvue-editor"
134
+ }, [
135
+ n("div", {
136
+ ref_key: "editorRef",
137
+ ref: l,
138
+ class: O(["cropvue-editor__viewport", { "cropvue-editor__viewport--panning": m.value }]),
139
+ tabindex: "0",
140
+ onPointerup: re
141
+ }, [
142
+ x(r.$slots, "image", {
143
+ style: Y(X.value),
144
+ image: f.image,
145
+ transform: f.transform
146
+ }, () => [
147
+ f.image ? (w(), C("img", {
148
+ key: 0,
149
+ src: f.image.element.src,
150
+ class: "cropvue-editor__image",
151
+ style: Y(X.value),
152
+ draggable: "false",
153
+ alt: ""
154
+ }, null, 12, we)) : I("", !0)
155
+ ]),
156
+ x(r.$slots, "overlay", {
157
+ crop: f.crop,
158
+ clipPath: j.value
159
+ }, () => [
160
+ n("div", {
161
+ class: "cropvue-editor__overlay",
162
+ style: Y(A.value)
163
+ }, null, 4)
164
+ ]),
165
+ x(r.$slots, "crop-area", {
166
+ crop: f.crop,
167
+ style: Y(q.value)
168
+ }, () => [
169
+ n("div", {
170
+ class: O(["cropvue-editor__crop-area", { "cropvue-editor__crop-area--circle": f.crop.stencil === "circle" }]),
171
+ style: Y(q.value)
172
+ }, [
173
+ f.crop.stencil !== "circle" ? x(r.$slots, "grid", {
174
+ key: 0,
175
+ crop: f.crop
176
+ }, () => [
177
+ a[0] || (a[0] = oe('<div class="cropvue-editor__grid"><div class="cropvue-editor__grid-line cropvue-editor__grid-line--h1"></div><div class="cropvue-editor__grid-line cropvue-editor__grid-line--h2"></div><div class="cropvue-editor__grid-line cropvue-editor__grid-line--v1"></div><div class="cropvue-editor__grid-line cropvue-editor__grid-line--v2"></div></div>', 1))
178
+ ]) : I("", !0),
179
+ x(r.$slots, "handles", { crop: f.crop }, () => [
180
+ f.crop.stencil === "circle" ? (w(), C("div", Ce)) : (w(), C(G, { key: 1 }, [
181
+ a[1] || (a[1] = n("div", {
182
+ class: "cropvue-editor__handle cropvue-editor__handle--nw",
183
+ "data-handle": "nw"
184
+ }, null, -1)),
185
+ a[2] || (a[2] = n("div", {
186
+ class: "cropvue-editor__handle cropvue-editor__handle--ne",
187
+ "data-handle": "ne"
188
+ }, null, -1)),
189
+ a[3] || (a[3] = n("div", {
190
+ class: "cropvue-editor__handle cropvue-editor__handle--sw",
191
+ "data-handle": "sw"
192
+ }, null, -1)),
193
+ a[4] || (a[4] = n("div", {
194
+ class: "cropvue-editor__handle cropvue-editor__handle--se",
195
+ "data-handle": "se"
196
+ }, null, -1))
197
+ ], 64))
198
+ ])
199
+ ], 6)
200
+ ])
201
+ ], 34)
202
+ ], 512));
203
+ }
204
+ }), Re = { class: "cropvue-toolbar" }, Me = /* @__PURE__ */ V({
205
+ __name: "CropToolbar",
206
+ props: {
207
+ transform: {}
208
+ },
209
+ emits: ["rotate-left", "rotate-right", "flip-x", "flip-y", "zoom-in", "zoom-out", "reset"],
210
+ setup(f, { emit: k }) {
211
+ const v = k;
212
+ function e() {
213
+ v("rotate-left");
214
+ }
215
+ function o() {
216
+ v("rotate-right");
217
+ }
218
+ function l() {
219
+ v("flip-x");
220
+ }
221
+ function t() {
222
+ v("flip-y");
223
+ }
224
+ function s() {
225
+ v("zoom-in");
226
+ }
227
+ function p() {
228
+ v("zoom-out");
229
+ }
230
+ function m() {
231
+ v("reset");
232
+ }
233
+ return (u, c) => (w(), C("div", Re, [
234
+ x(u.$slots, "default", {
235
+ rotateLeft: e,
236
+ rotateRight: o,
237
+ flipX: l,
238
+ flipY: t,
239
+ zoomIn: s,
240
+ zoomOut: p,
241
+ reset: m,
242
+ transform: f.transform
243
+ }, () => [
244
+ n("div", { class: "cropvue-toolbar__default" }, [
245
+ n("button", {
246
+ type: "button",
247
+ class: "cropvue-toolbar__btn",
248
+ title: "Rotate left",
249
+ onClick: e
250
+ }, [...c[0] || (c[0] = [
251
+ n("svg", {
252
+ viewBox: "0 0 24 24",
253
+ width: "20",
254
+ height: "20",
255
+ fill: "none",
256
+ stroke: "currentColor",
257
+ "stroke-width": "2"
258
+ }, [
259
+ n("path", { d: "M2.5 2v6h6M2.66 12a9 9 0 1 0 1.18-4.5" })
260
+ ], -1)
261
+ ])]),
262
+ n("button", {
263
+ type: "button",
264
+ class: "cropvue-toolbar__btn",
265
+ title: "Rotate right",
266
+ onClick: o
267
+ }, [...c[1] || (c[1] = [
268
+ n("svg", {
269
+ viewBox: "0 0 24 24",
270
+ width: "20",
271
+ height: "20",
272
+ fill: "none",
273
+ stroke: "currentColor",
274
+ "stroke-width": "2"
275
+ }, [
276
+ n("path", { d: "M21.5 2v6h-6M21.34 12a9 9 0 1 1-1.18-4.5" })
277
+ ], -1)
278
+ ])]),
279
+ c[7] || (c[7] = n("span", { class: "cropvue-toolbar__separator" }, null, -1)),
280
+ n("button", {
281
+ type: "button",
282
+ class: "cropvue-toolbar__btn",
283
+ title: "Flip horizontal",
284
+ onClick: l
285
+ }, [...c[2] || (c[2] = [
286
+ n("svg", {
287
+ viewBox: "0 0 24 24",
288
+ width: "20",
289
+ height: "20",
290
+ fill: "none",
291
+ stroke: "currentColor",
292
+ "stroke-width": "2"
293
+ }, [
294
+ n("path", { d: "M8 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h3M16 3h3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-3M12 20V4" })
295
+ ], -1)
296
+ ])]),
297
+ n("button", {
298
+ type: "button",
299
+ class: "cropvue-toolbar__btn",
300
+ title: "Flip vertical",
301
+ onClick: t
302
+ }, [...c[3] || (c[3] = [
303
+ n("svg", {
304
+ viewBox: "0 0 24 24",
305
+ width: "20",
306
+ height: "20",
307
+ fill: "none",
308
+ stroke: "currentColor",
309
+ "stroke-width": "2"
310
+ }, [
311
+ n("path", { d: "M3 8V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v3M3 16v3a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-3M20 12H4" })
312
+ ], -1)
313
+ ])]),
314
+ c[8] || (c[8] = n("span", { class: "cropvue-toolbar__separator" }, null, -1)),
315
+ n("button", {
316
+ type: "button",
317
+ class: "cropvue-toolbar__btn",
318
+ title: "Zoom out",
319
+ onClick: p
320
+ }, [...c[4] || (c[4] = [
321
+ n("svg", {
322
+ viewBox: "0 0 24 24",
323
+ width: "20",
324
+ height: "20",
325
+ fill: "none",
326
+ stroke: "currentColor",
327
+ "stroke-width": "2"
328
+ }, [
329
+ n("circle", {
330
+ cx: "11",
331
+ cy: "11",
332
+ r: "8"
333
+ }),
334
+ n("line", {
335
+ x1: "21",
336
+ y1: "21",
337
+ x2: "16.65",
338
+ y2: "16.65"
339
+ }),
340
+ n("line", {
341
+ x1: "8",
342
+ y1: "11",
343
+ x2: "14",
344
+ y2: "11"
345
+ })
346
+ ], -1)
347
+ ])]),
348
+ n("button", {
349
+ type: "button",
350
+ class: "cropvue-toolbar__btn",
351
+ title: "Zoom in",
352
+ onClick: s
353
+ }, [...c[5] || (c[5] = [
354
+ oe('<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>', 1)
355
+ ])]),
356
+ c[9] || (c[9] = n("span", { class: "cropvue-toolbar__separator" }, null, -1)),
357
+ n("button", {
358
+ type: "button",
359
+ class: "cropvue-toolbar__btn",
360
+ title: "Reset",
361
+ onClick: m
362
+ }, [...c[6] || (c[6] = [
363
+ n("svg", {
364
+ viewBox: "0 0 24 24",
365
+ width: "20",
366
+ height: "20",
367
+ fill: "none",
368
+ stroke: "currentColor",
369
+ "stroke-width": "2"
370
+ }, [
371
+ n("polyline", { points: "1 4 1 10 7 10" }),
372
+ n("polyline", { points: "23 20 23 14 17 14" }),
373
+ n("path", { d: "M20.49 9A9 9 0 0 0 5.64 5.64L1 10M23 14l-4.64 4.36A9 9 0 0 1 3.51 15" })
374
+ ], -1)
375
+ ])])
376
+ ])
377
+ ])
378
+ ]));
379
+ }
380
+ }), He = { class: "cropvue" }, We = { class: "cropvue__actions" }, Pe = ["disabled"], Se = { class: "cropvue__done" }, Be = ["src"], Qe = /* @__PURE__ */ V({
381
+ __name: "CropVue",
382
+ props: {
383
+ stencil: { default: "rectangle" },
384
+ aspectRatio: { default: null },
385
+ minWidth: { default: 0 },
386
+ minHeight: { default: 0 },
387
+ maxWidth: { default: 1 / 0 },
388
+ maxHeight: { default: 1 / 0 },
389
+ outputFormat: { default: "auto" },
390
+ outputQuality: { default: 0.85 },
391
+ outputMaxWidth: { default: void 0 },
392
+ outputMaxHeight: { default: void 0 },
393
+ accept: { default: () => ["image/*"] },
394
+ maxFileSize: { default: 1 / 0 },
395
+ multiple: { type: Boolean, default: !1 },
396
+ upload: { type: [Function, null], default: null },
397
+ src: { default: null },
398
+ modelValue: { default: null },
399
+ pannable: { type: Boolean, default: !0 }
400
+ },
401
+ emits: ["ready", "change", "done", "cancel", "remove", "uploaded", "error", "queue-change", "update:modelValue"],
402
+ setup(f, { expose: k, emit: v }) {
403
+ const e = f, o = v, l = D("dropzone"), t = xe({
404
+ stencil: e.stencil,
405
+ aspectRatio: e.aspectRatio ?? void 0,
406
+ minWidth: e.minWidth,
407
+ minHeight: e.minHeight,
408
+ maxWidth: e.maxWidth,
409
+ maxHeight: e.maxHeight,
410
+ outputFormat: e.outputFormat,
411
+ outputQuality: e.outputQuality,
412
+ outputMaxWidth: e.outputMaxWidth,
413
+ outputMaxHeight: e.outputMaxHeight
414
+ }), s = ne({
415
+ accept: e.accept,
416
+ maxSize: e.maxFileSize,
417
+ multiple: e.multiple,
418
+ onFiles: N,
419
+ onError: (d) => o("error", d)
420
+ }), p = s.dropzoneRef, m = D(null), u = D(!1), c = D(0);
421
+ async function N(d) {
422
+ if (d.length !== 0)
423
+ try {
424
+ await t.loadFile(d[0]), l.value = "editor", t.image.value && o("ready", {
425
+ width: t.image.value.naturalWidth,
426
+ height: t.image.value.naturalHeight
427
+ });
428
+ } catch {
429
+ o("error", { type: "load-failed", message: "Failed to load image" });
430
+ }
431
+ }
432
+ Z(() => e.src, async (d) => {
433
+ if (d)
434
+ try {
435
+ await t.loadUrl(d), l.value = "editor", t.image.value && o("ready", {
436
+ width: t.image.value.naturalWidth,
437
+ height: t.image.value.naturalHeight
438
+ });
439
+ } catch {
440
+ o("error", { type: "load-failed", message: "Failed to load image from URL" });
441
+ }
442
+ }, { immediate: !0 }), Z(() => e.stencil, (d) => t.setStencil(d)), Z(() => e.aspectRatio, (d) => t.setAspectRatio(d ?? null));
443
+ async function B() {
444
+ try {
445
+ const d = await t.getResult({
446
+ format: e.outputFormat,
447
+ quality: e.outputQuality,
448
+ maxWidth: e.outputMaxWidth,
449
+ maxHeight: e.outputMaxHeight
450
+ });
451
+ if (m.value = d, o("done", d), o("update:modelValue", d), e.upload) {
452
+ u.value = !0, c.value = 0;
453
+ const _ = new AbortController();
454
+ try {
455
+ const H = await e.upload(d.file, {
456
+ onProgress: (T) => {
457
+ c.value = T;
458
+ },
459
+ signal: _.signal
460
+ });
461
+ o("uploaded", H);
462
+ } catch (H) {
463
+ o("error", { type: "upload-failed", message: String(H) });
464
+ } finally {
465
+ u.value = !1;
466
+ }
467
+ }
468
+ l.value = "done";
469
+ } catch (d) {
470
+ o("error", { type: "compress-failed", message: String(d) });
471
+ }
472
+ }
473
+ function X() {
474
+ o("cancel"), m.value ? l.value = "done" : (l.value = "dropzone", t.reset());
475
+ }
476
+ function q() {
477
+ l.value = "dropzone", m.value = null, t.reset();
478
+ }
479
+ function A() {
480
+ o("remove"), o("update:modelValue", null), l.value = "dropzone", m.value = null, t.reset();
481
+ }
482
+ function j() {
483
+ l.value = "editor";
484
+ }
485
+ return k({
486
+ cropper: t,
487
+ phase: l,
488
+ confirm: B,
489
+ cancel: X,
490
+ restart: q,
491
+ reedit: j,
492
+ remove: A,
493
+ result: m
494
+ }), (d, _) => (w(), C("div", He, [
495
+ l.value === "dropzone" ? (w(), C("div", {
496
+ key: 0,
497
+ ref_key: "dropzoneRef",
498
+ ref: p
499
+ }, [
500
+ x(d.$slots, "dropzone", {
501
+ open: i(s).open,
502
+ isDragging: i(s).isDragging.value
503
+ }, () => [
504
+ n("div", {
505
+ class: O(["cropvue__dropzone", { "cropvue__dropzone--active": i(s).isDragging.value }]),
506
+ onClick: _[0] || (_[0] = //@ts-ignore
507
+ (...H) => i(s).open && i(s).open(...H))
508
+ }, [
509
+ x(d.$slots, "dropzone-content", {
510
+ open: i(s).open,
511
+ isDragging: i(s).isDragging.value
512
+ }, () => [
513
+ _[5] || (_[5] = n("p", null, "Drop image here or click to select", -1))
514
+ ])
515
+ ], 2)
516
+ ])
517
+ ], 512)) : I("", !0),
518
+ l.value === "editor" ? (w(), C(G, { key: 1 }, [
519
+ x(d.$slots, "editor", {
520
+ image: i(t).image.value,
521
+ transform: i(t).transform.value,
522
+ crop: i(t).crop.value,
523
+ rotateLeft: i(t).rotateLeft,
524
+ rotateRight: i(t).rotateRight,
525
+ flipX: i(t).flipX,
526
+ flipY: i(t).flipY,
527
+ zoomIn: () => i(t).zoomBy(0.1),
528
+ zoomOut: () => i(t).zoomBy(-0.1),
529
+ reset: i(t).reset,
530
+ confirm: B,
531
+ cancel: X,
532
+ remove: A,
533
+ pannable: f.pannable
534
+ }, () => [
535
+ J(ze, {
536
+ image: i(t).image.value,
537
+ transform: i(t).transform.value,
538
+ crop: i(t).crop.value,
539
+ pannable: f.pannable,
540
+ "onUpdate:transform": _[1] || (_[1] = (H) => i(t).transform.value = H),
541
+ "onUpdate:crop": _[2] || (_[2] = (H) => i(t).crop.value = H)
542
+ }, null, 8, ["image", "transform", "crop", "pannable"])
543
+ ]),
544
+ x(d.$slots, "toolbar", {
545
+ rotateLeft: i(t).rotateLeft,
546
+ rotateRight: i(t).rotateRight,
547
+ flipX: i(t).flipX,
548
+ flipY: i(t).flipY,
549
+ zoomIn: () => i(t).zoomBy(0.1),
550
+ zoomOut: () => i(t).zoomBy(-0.1),
551
+ reset: i(t).reset,
552
+ transform: i(t).transform.value
553
+ }, () => [
554
+ J(Me, {
555
+ transform: i(t).transform.value,
556
+ onRotateLeft: i(t).rotateLeft,
557
+ onRotateRight: i(t).rotateRight,
558
+ onFlipX: i(t).flipX,
559
+ onFlipY: i(t).flipY,
560
+ onZoomIn: _[3] || (_[3] = () => i(t).zoomBy(0.1)),
561
+ onZoomOut: _[4] || (_[4] = () => i(t).zoomBy(-0.1)),
562
+ onReset: i(t).reset
563
+ }, null, 8, ["transform", "onRotateLeft", "onRotateRight", "onFlipX", "onFlipY", "onReset"])
564
+ ]),
565
+ x(d.$slots, "preview", {
566
+ image: i(t).image.value,
567
+ transform: i(t).transform.value,
568
+ crop: i(t).crop.value
569
+ }),
570
+ x(d.$slots, "actions", {
571
+ confirm: B,
572
+ cancel: X,
573
+ remove: A,
574
+ isUploading: u.value,
575
+ progress: c.value
576
+ }, () => [
577
+ n("div", We, [
578
+ n("button", {
579
+ type: "button",
580
+ class: "cropvue__btn cropvue__btn--cancel",
581
+ onClick: X
582
+ }, "Cancel"),
583
+ n("button", {
584
+ type: "button",
585
+ class: "cropvue__btn cropvue__btn--confirm",
586
+ disabled: u.value,
587
+ onClick: B
588
+ }, ce(u.value ? "Uploading..." : "Confirm"), 9, Pe)
589
+ ])
590
+ ])
591
+ ], 64)) : I("", !0),
592
+ l.value === "done" ? x(d.$slots, "done", {
593
+ key: 2,
594
+ result: m.value,
595
+ restart: q,
596
+ reedit: j,
597
+ remove: A
598
+ }, () => [
599
+ n("div", Se, [
600
+ m.value ? (w(), C("img", {
601
+ key: 0,
602
+ src: m.value.url,
603
+ alt: "Cropped result",
604
+ class: "cropvue__result-image"
605
+ }, null, 8, Be)) : I("", !0),
606
+ n("button", {
607
+ type: "button",
608
+ class: "cropvue__btn",
609
+ onClick: q
610
+ }, "Crop another")
611
+ ])
612
+ ]) : I("", !0),
613
+ x(d.$slots, "error"),
614
+ u.value ? x(d.$slots, "loading", {
615
+ key: 3,
616
+ progress: c.value
617
+ }) : I("", !0)
618
+ ]));
619
+ }
620
+ }), Fe = { class: "cropvue-preview" }, Ee = /* @__PURE__ */ V({
621
+ __name: "CropPreview",
622
+ props: {
623
+ image: {},
624
+ transform: {},
625
+ crop: {},
626
+ maxWidth: { default: void 0 },
627
+ maxHeight: { default: void 0 },
628
+ debounce: { default: 50 }
629
+ },
630
+ setup(f, { expose: k }) {
631
+ const v = f, e = D(null);
632
+ let o;
633
+ function l() {
634
+ const s = e.value, p = v.image;
635
+ !s || !p || $e(s, p.element, v.crop, v.transform, {
636
+ maxWidth: v.maxWidth,
637
+ maxHeight: v.maxHeight
638
+ });
639
+ }
640
+ function t() {
641
+ o && clearTimeout(o), o = setTimeout(l, v.debounce);
642
+ }
643
+ return Z([() => v.transform, () => v.crop, () => v.image], t, {
644
+ deep: !0
645
+ }), ee(() => {
646
+ v.image && l();
647
+ }), te(() => {
648
+ o && clearTimeout(o);
649
+ }), k({ canvasRef: e, render: l }), (s, p) => (w(), C("div", Fe, [
650
+ x(s.$slots, "default", {
651
+ canvasRef: e.value,
652
+ render: l
653
+ }, () => [
654
+ n("canvas", {
655
+ ref_key: "canvasRef",
656
+ ref: e,
657
+ class: "cropvue-preview__canvas"
658
+ }, null, 512)
659
+ ])
660
+ ]));
661
+ }
662
+ }), Ue = /* @__PURE__ */ V({
663
+ __name: "CropDropzone",
664
+ props: {
665
+ accept: { default: () => ["image/*"] },
666
+ maxSize: { default: 1 / 0 },
667
+ multiple: { type: Boolean, default: !1 }
668
+ },
669
+ emits: ["files", "error"],
670
+ setup(f, { expose: k, emit: v }) {
671
+ const e = f, o = v, { isDragging: l, files: t, dropzoneRef: s, open: p } = ne({
672
+ accept: e.accept,
673
+ maxSize: e.maxSize,
674
+ multiple: e.multiple,
675
+ onFiles: (m) => o("files", m),
676
+ onError: (m) => o("error", m)
677
+ });
678
+ return k({ open: p, files: t }), (m, u) => (w(), C("div", {
679
+ ref_key: "dropzoneRef",
680
+ ref: s,
681
+ class: O(["cropvue-dropzone", { "cropvue-dropzone--active": i(l) }])
682
+ }, [
683
+ x(m.$slots, "default", {
684
+ open: i(p),
685
+ isDragging: i(l)
686
+ }, () => [
687
+ n("div", {
688
+ class: "cropvue-dropzone__default",
689
+ onClick: u[0] || (u[0] = //@ts-ignore
690
+ (...c) => i(p) && i(p)(...c))
691
+ }, [...u[1] || (u[1] = [
692
+ n("p", null, "Drop image here or click to select", -1)
693
+ ])])
694
+ ])
695
+ ], 2));
696
+ }
697
+ }), Le = { class: "cropvue-queue" }, De = { class: "cropvue-queue__list" }, Ie = ["onClick"], Xe = ["src", "alt"], Ye = ["onClick"], Ve = {
698
+ key: 0,
699
+ class: "cropvue-queue__check"
700
+ }, Ze = /* @__PURE__ */ V({
701
+ __name: "CropQueue",
702
+ emits: ["select", "remove", "change"],
703
+ setup(f, { expose: k, emit: v }) {
704
+ const e = v, o = be();
705
+ function l(s) {
706
+ const p = o.images.value[s];
707
+ p && (o.select(s), e("select", p));
708
+ }
709
+ function t(s) {
710
+ const p = o.images.value[s];
711
+ p && (o.remove(s), e("remove", p), e("change", o.images.value));
712
+ }
713
+ return k({ queue: o }), (s, p) => (w(), C("div", Le, [
714
+ x(s.$slots, "default", {
715
+ items: i(o).images.value,
716
+ currentIndex: i(o).current.value,
717
+ select: l,
718
+ remove: t,
719
+ add: i(o).add
720
+ }, () => [
721
+ n("div", De, [
722
+ (w(!0), C(G, null, pe(i(o).images.value, (m, u) => (w(), C("div", {
723
+ key: m.id,
724
+ class: O(["cropvue-queue__item", {
725
+ "cropvue-queue__item--active": i(o).current.value === u,
726
+ "cropvue-queue__item--done": m.status === "done"
727
+ }]),
728
+ onClick: (c) => l(u)
729
+ }, [
730
+ n("img", {
731
+ src: m.thumbnail,
732
+ alt: `Queue item ${u + 1}`,
733
+ class: "cropvue-queue__thumbnail"
734
+ }, null, 8, Xe),
735
+ n("button", {
736
+ type: "button",
737
+ class: "cropvue-queue__remove",
738
+ title: "Remove",
739
+ onClick: de((c) => t(u), ["stop"])
740
+ }, [...p[0] || (p[0] = [
741
+ n("svg", {
742
+ viewBox: "0 0 24 24",
743
+ width: "14",
744
+ height: "14",
745
+ fill: "none",
746
+ stroke: "currentColor",
747
+ "stroke-width": "2"
748
+ }, [
749
+ n("line", {
750
+ x1: "18",
751
+ y1: "6",
752
+ x2: "6",
753
+ y2: "18"
754
+ }),
755
+ n("line", {
756
+ x1: "6",
757
+ y1: "6",
758
+ x2: "18",
759
+ y2: "18"
760
+ })
761
+ ], -1)
762
+ ])], 8, Ye),
763
+ m.status === "done" ? (w(), C("div", Ve, [...p[1] || (p[1] = [
764
+ n("svg", {
765
+ viewBox: "0 0 24 24",
766
+ width: "16",
767
+ height: "16",
768
+ fill: "none",
769
+ stroke: "currentColor",
770
+ "stroke-width": "3"
771
+ }, [
772
+ n("polyline", { points: "20 6 9 17 4 12" })
773
+ ], -1)
774
+ ])])) : I("", !0)
775
+ ], 10, Ie))), 128))
776
+ ])
777
+ ])
778
+ ]));
779
+ }
780
+ }), qe = { class: "cropvue-stencil" }, Oe = /* @__PURE__ */ V({
781
+ __name: "CropStencil",
782
+ props: {
783
+ crop: {},
784
+ containerWidth: {},
785
+ containerHeight: {}
786
+ },
787
+ setup(f) {
788
+ const k = f, v = L(() => {
789
+ const o = k.crop;
790
+ if (o.stencil === "circle") {
791
+ const m = Math.min(o.width, o.height) / 2, u = o.x + o.width / 2, c = o.y + o.height / 2;
792
+ return `circle(${m}px at ${u}px ${c}px)`;
793
+ }
794
+ if (o.stencil === "freeform" && o.points && o.points.length >= 3)
795
+ return `polygon(${o.points.map((u) => `${u.x}px ${u.y}px`).join(", ")})`;
796
+ const l = o.y, t = k.containerWidth - (o.x + o.width), s = k.containerHeight - (o.y + o.height), p = o.x;
797
+ return `inset(${l}px ${t}px ${s}px ${p}px)`;
798
+ }), e = L(() => ({
799
+ clipPath: v.value,
800
+ WebkitClipPath: v.value
801
+ }));
802
+ return (o, l) => (w(), C("div", qe, [
803
+ x(o.$slots, "default", {
804
+ clipPath: v.value,
805
+ style: Y(e.value),
806
+ crop: f.crop,
807
+ stencil: f.crop.stencil,
808
+ points: f.crop.points
809
+ }, () => [
810
+ n("div", {
811
+ class: "cropvue-stencil__shape",
812
+ style: Y(e.value)
813
+ }, null, 4)
814
+ ])
815
+ ]));
816
+ }
817
+ });
818
+ export {
819
+ Ue as CropDropzone,
820
+ ze as CropEditor,
821
+ Ee as CropPreview,
822
+ Ze as CropQueue,
823
+ Oe as CropStencil,
824
+ Me as CropToolbar,
825
+ Qe as CropVue,
826
+ Ke as useCompressor,
827
+ Ge as useCropper,
828
+ Je as useDropzone,
829
+ et as useImageQueue,
830
+ tt as useUploader
831
+ };