@lmb-it/kitsconcerto-native 0.1.286 → 0.1.288

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.
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import React, { useState, useRef, useCallback, useMemo, useReducer, useEffect } from 'react';
3
- import { PanResponder, View, Text, Image, Pressable } from 'react-native';
3
+ import { PanResponder, View, Text, Image, ActivityIndicator, Pressable } from 'react-native';
4
4
  import Slider from '@react-native-community/slider';
5
5
  import { Icon } from '../../primitives/Icon/index.js';
6
6
  import '../../contexts/DialogContext.js';
@@ -11,7 +11,7 @@ import useResolvedElementStyles from '../../hooks/useResolvedElementStyles.js';
11
11
  import { cropperReducer, initialCropperState, scaleRange, zoomFraction } from './cropperState.js';
12
12
  import { aspectOf, cropToSourceRect, orientedSize } from './geometry.js';
13
13
  import { isUsablePinchReading, pinchScale } from './pinchZoom.js';
14
- import { DEFAULT_ASPECT, DEFAULT_MAX_ZOOM, DEFAULT_FORMAT, DEFAULT_MAX_BYTES, DEFAULT_LABELS } from './constants.js';
14
+ import { DEFAULT_ASPECT, DEFAULT_MAX_ZOOM, SQUARE_CROP_FACTOR, STAGE_INSET_NATIVE, DEFAULT_FORMAT, DEFAULT_MAX_BYTES, STAGE_ASPECT_NATIVE, CROP_ACCENT_SOFT, DEFAULT_LABELS, CROP_DANGER, CROP_ACCENT } from './constants.js';
15
15
  import { useImageSource } from './useImageSource.js';
16
16
  import { encodeCropNative } from './encode.js';
17
17
  import { hasCropEncoder } from './encoderRegistry.js';
@@ -83,7 +83,9 @@ function ImageCropper(rawProps) {
83
83
  ratio,
84
84
  maxZoom,
85
85
  // ⚠️ ALWAYS FALSE ON NATIVE — see the note at the top of the file.
86
- resizable: false
86
+ resizable: false,
87
+ inset: STAGE_INSET_NATIVE,
88
+ squareFactor: SQUARE_CROP_FACTOR
87
89
  }),
88
90
  [image, viewport, ratio, maxZoom]
89
91
  );
@@ -248,186 +250,388 @@ function ImageCropper(rawProps) {
248
250
  const { crop, transform, rotation } = state;
249
251
  const oriented = image ? orientedSize({ width: image.width, height: image.height }, rotation) : null;
250
252
  const band = scaleRange(state);
251
- const zoomLabel = Number.isFinite(band.min) && band.min > 0 ? `${(transform.scale / band.min).toFixed(1)}\xD7` : "1.0\xD7";
253
+ const zoomLabel = Number.isFinite(band.min) && band.min > 0 ? `${Math.round(transform.scale / band.min * 100)}%` : "100%";
252
254
  const windowRadius = isCircle ? Math.max(crop.width, crop.height) / 2 : Number(el.mask?.borderRadius ?? 0);
253
255
  const SCRIM = Math.ceil(Math.max(viewport.width, viewport.height)) || 1;
254
- const toolButton = (icon, label, onPress, disabled) => /* @__PURE__ */ jsxs(
256
+ const aspectLabel = `${aspect[0]}:${aspect[1]}`;
257
+ const subtitle = ready ? "Drag to move \xB7 pinch to zoom" : isCircle ? `Circle \xB7 ${aspectLabel}` : `Banner \xB7 ${aspectLabel}`;
258
+ const budgetKb = Math.round((output?.maxBytes ?? DEFAULT_MAX_BYTES) / 1024);
259
+ const previewWidth = isCircle ? 60 : 160;
260
+ const previewHeight = isCircle ? 60 : Math.round(160 / ratio);
261
+ const stageTool = (icon, label, onPress) => /* @__PURE__ */ jsxs(
255
262
  Pressable,
256
263
  {
257
264
  onPress,
258
- disabled,
265
+ disabled: !ready,
259
266
  accessibilityRole: "button",
260
267
  accessibilityLabel: label,
261
- accessibilityState: { disabled: !!disabled },
262
- style: {
263
- flexDirection: "row",
264
- alignItems: "center",
265
- gap: 6,
266
- paddingHorizontal: 8,
267
- paddingVertical: 8,
268
- opacity: disabled ? 0.5 : 1
269
- },
268
+ accessibilityState: { disabled: !ready },
269
+ style: { width: 58, alignItems: "center", gap: 4 },
270
270
  children: [
271
- /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm", color: el.icon?.color }),
272
- /* @__PURE__ */ jsx(Text, { style: el.text, children: label })
271
+ /* @__PURE__ */ jsx(View, { style: {
272
+ width: 44,
273
+ height: 44,
274
+ borderRadius: 22,
275
+ backgroundColor: "rgba(255,255,255,0.09)",
276
+ alignItems: "center",
277
+ justifyContent: "center"
278
+ }, children: /* @__PURE__ */ jsx(Icon, { name: icon, size: 19, color: "#FFFFFF" }) }),
279
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 11, fontWeight: "600", color: "rgba(255,255,255,0.75)" }, children: label })
273
280
  ]
274
281
  }
275
282
  );
276
- return /* @__PURE__ */ jsxs(View, { testID, style: [{ width: "100%" }, el.root], children: [
277
- title != null && /* @__PURE__ */ jsx(View, { style: [{ flexDirection: "row", alignItems: "center" }, el.header], children: /* @__PURE__ */ jsx(Text, { style: el.title, children: title }) }),
278
- /* @__PURE__ */ jsx(View, { style: [el.content], children: !source || error ? /* @__PURE__ */ jsxs(View, { style: [{ alignItems: "center" }, el.dropZone], children: [
279
- /* @__PURE__ */ jsx(Icon, { name: "image", size: "lg", color: el.icon?.color }),
280
- /* @__PURE__ */ jsx(Text, { style: el.text, children: error ? error.message : text.dropZone })
281
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
282
- /* @__PURE__ */ jsx(StageFrame, { gesture: detector, children: /* @__PURE__ */ jsxs(
283
- View,
284
- {
285
- ref: stageRef,
286
- onLayout: onStageLayout,
287
- ...detector ? scrollBlocker.panHandlers : responder.panHandlers,
288
- style: [{ width: "100%", aspectRatio: 4 / 3, overflow: "hidden" }, el.container],
289
- children: [
290
- ready && image && oriented && /* @__PURE__ */ jsx(
291
- Image,
292
- {
293
- source: { uri: image.uri },
294
- resizeMode: "stretch",
295
- style: {
296
- position: "absolute",
297
- left: transform.x,
298
- top: transform.y,
299
- width: oriented.width * transform.scale,
300
- height: oriented.height * transform.scale,
301
- transform: [{ rotate: `${rotation}deg` }]
302
- }
303
- }
304
- ),
305
- ready && /* @__PURE__ */ jsxs(Fragment, { children: [
306
- /* @__PURE__ */ jsx(
307
- View,
308
- {
309
- pointerEvents: "none",
310
- style: [
311
- {
283
+ return /* @__PURE__ */ jsxs(
284
+ View,
285
+ {
286
+ testID,
287
+ style: [
288
+ /**
289
+ * ⚠️ NO `flex: 1` HERE, THOUGH THE MOCKUP IS FULL-SCREEN. A
290
+ * child with `flex: 1` inside a parent that was never given a
291
+ * height measures 0 on Yoga, and this component does not own
292
+ * the screen — a consumer mounts it in a modal, a sheet or a
293
+ * form. The footer's `marginTop: 'auto'` gets the mockup's
294
+ * pinned-to-the-bottom footer when the parent DOES bound the
295
+ * height, and does nothing when it does not.
296
+ */
297
+ { width: "100%" },
298
+ el.root,
299
+ /**
300
+ * ⛔ THE PHONE CROPPER IS A DARK SHEET, AND THAT OVERRIDES THE
301
+ * `root` SLOT ON NATIVE ONLY. The mockup draws web as a light
302
+ * modal card and the phone as a full-screen dark surface, and
303
+ * one `root` token cannot be both. It has to be the dark one
304
+ * here because the controls below it are load-bearing white:
305
+ * on a light sheet the Rotate and Reset labels vanish.
306
+ *
307
+ * ⚠️ SO `slots.root.backgroundColor` DOES NOT REACH THIS ON
308
+ * NATIVE. That is the deliberate cost; the stage's own colour
309
+ * has carried the same argument since the component shipped.
310
+ */
311
+ { backgroundColor: "#0B1517" }
312
+ ],
313
+ children: [
314
+ title != null && /* @__PURE__ */ jsxs(View, { style: [
315
+ { alignItems: "center" },
316
+ el.header,
317
+ {
318
+ paddingLeft: 18,
319
+ paddingRight: 18,
320
+ paddingTop: 12,
321
+ paddingBottom: 14,
322
+ borderBottomWidth: 0,
323
+ gap: 0
324
+ }
325
+ ], children: [
326
+ /* @__PURE__ */ jsx(Text, { style: [el.title, { fontSize: 16, fontWeight: "800", color: "#FFFFFF" }], children: title }),
327
+ /* @__PURE__ */ jsx(Text, { style: { marginTop: 3, fontSize: 12, color: "rgba(255,255,255,0.6)" }, children: subtitle })
328
+ ] }),
329
+ /* @__PURE__ */ jsxs(View, { style: [el.content, { paddingLeft: 0, paddingRight: 0, paddingTop: 0, paddingBottom: 0 }], children: [
330
+ /* @__PURE__ */ jsx(StageFrame, { gesture: detector, children: /* @__PURE__ */ jsxs(
331
+ View,
332
+ {
333
+ ref: stageRef,
334
+ onLayout: onStageLayout,
335
+ ...detector ? scrollBlocker.panHandlers : responder.panHandlers,
336
+ style: [
337
+ { width: "100%", aspectRatio: STAGE_ASPECT_NATIVE, overflow: "hidden" },
338
+ el.container
339
+ ],
340
+ children: [
341
+ ready && image && oriented && /* @__PURE__ */ jsx(
342
+ Image,
343
+ {
344
+ source: { uri: image.uri },
345
+ resizeMode: "stretch",
346
+ style: {
312
347
  position: "absolute",
313
- left: crop.x - SCRIM,
314
- top: crop.y - SCRIM,
315
- width: crop.width + SCRIM * 2,
316
- height: crop.height + SCRIM * 2,
317
- borderWidth: SCRIM,
318
- borderColor: el.shade?.backgroundColor ?? "rgba(0,0,0,0.6)",
319
- borderRadius: windowRadius + SCRIM
348
+ left: transform.x,
349
+ top: transform.y,
350
+ width: oriented.width * transform.scale,
351
+ height: oriented.height * transform.scale,
352
+ transform: [{ rotate: `${rotation}deg` }]
353
+ }
354
+ }
355
+ ),
356
+ (!source || error) && /* @__PURE__ */ jsxs(View, { style: [
357
+ {
358
+ position: "absolute",
359
+ left: 18,
360
+ top: 18,
361
+ right: 18,
362
+ bottom: 18,
363
+ alignItems: "center",
364
+ justifyContent: "center"
365
+ },
366
+ el.dropZone,
367
+ { borderRadius: 18 }
368
+ ], children: [
369
+ /* @__PURE__ */ jsx(View, { style: {
370
+ width: 60,
371
+ height: 60,
372
+ borderRadius: 30,
373
+ backgroundColor: "rgba(32,170,176,0.16)",
374
+ alignItems: "center",
375
+ justifyContent: "center"
376
+ }, children: /* @__PURE__ */ jsx(Icon, { name: "image", size: 26, color: CROP_ACCENT_SOFT }) }),
377
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 16, fontWeight: "800", color: "#FFFFFF", textAlign: "center" }, children: text.dropZone }),
378
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 12.5, color: "rgba(255,255,255,0.6)", textAlign: "center" }, children: "JPG, PNG or WebP" }),
379
+ error && /* @__PURE__ */ jsx(Text, { style: {
380
+ fontSize: 12.5,
381
+ fontWeight: "600",
382
+ color: CROP_DANGER,
383
+ textAlign: "center"
384
+ }, children: error.message })
385
+ ] }),
386
+ ready && /* @__PURE__ */ jsxs(Fragment, { children: [
387
+ /* @__PURE__ */ jsx(
388
+ View,
389
+ {
390
+ pointerEvents: "none",
391
+ style: [
392
+ {
393
+ position: "absolute",
394
+ left: crop.x - SCRIM,
395
+ top: crop.y - SCRIM,
396
+ width: crop.width + SCRIM * 2,
397
+ height: crop.height + SCRIM * 2,
398
+ borderWidth: SCRIM,
399
+ borderColor: el.shade?.backgroundColor ?? "rgba(0,0,0,0.6)",
400
+ borderRadius: windowRadius + SCRIM
401
+ }
402
+ ]
403
+ }
404
+ ),
405
+ /* @__PURE__ */ jsx(
406
+ View,
407
+ {
408
+ pointerEvents: "none",
409
+ style: [
410
+ {
411
+ position: "absolute",
412
+ left: crop.x,
413
+ top: crop.y,
414
+ width: crop.width,
415
+ height: crop.height,
416
+ borderStyle: "solid"
417
+ },
418
+ el.cropWindow,
419
+ { borderRadius: windowRadius }
420
+ ],
421
+ children: !hideGrid && [1, 2].map((n) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
422
+ /* @__PURE__ */ jsx(View, { style: [
423
+ {
424
+ position: "absolute",
425
+ top: 0,
426
+ bottom: 0,
427
+ width: 1,
428
+ left: crop.width * n / 3
429
+ },
430
+ el.gridLine
431
+ ] }),
432
+ /* @__PURE__ */ jsx(View, { style: [
433
+ {
434
+ position: "absolute",
435
+ left: 0,
436
+ right: 0,
437
+ height: 1,
438
+ top: crop.height * n / 3
439
+ },
440
+ el.gridLine
441
+ ] })
442
+ ] }, n))
320
443
  }
321
- ]
322
- }
323
- ),
324
- /* @__PURE__ */ jsx(
325
- View,
326
- {
327
- pointerEvents: "none",
328
- style: [
444
+ ),
445
+ /* @__PURE__ */ jsx(
446
+ View,
329
447
  {
448
+ pointerEvents: "none",
449
+ style: { position: "absolute", top: 12, left: 0, right: 0, alignItems: "center" },
450
+ children: /* @__PURE__ */ jsx(
451
+ View,
452
+ {
453
+ style: [
454
+ { alignItems: "center", justifyContent: "center" },
455
+ el.zoomChip
456
+ ],
457
+ children: /* @__PURE__ */ jsx(Text, { style: {
458
+ color: el.zoomChip?.color ?? "#FFFFFF",
459
+ fontSize: 11.5,
460
+ fontWeight: "700"
461
+ }, children: zoomLabel })
462
+ }
463
+ )
464
+ }
465
+ )
466
+ ] }),
467
+ (loading || busy) && /* @__PURE__ */ jsxs(
468
+ View,
469
+ {
470
+ pointerEvents: "none",
471
+ style: {
330
472
  position: "absolute",
331
- left: crop.x,
332
- top: crop.y,
333
- width: crop.width,
334
- height: crop.height,
335
- borderStyle: "solid"
473
+ left: 0,
474
+ top: 0,
475
+ right: 0,
476
+ bottom: 0,
477
+ backgroundColor: "rgba(6,14,16,0.74)",
478
+ alignItems: "center",
479
+ justifyContent: "center",
480
+ gap: 14
336
481
  },
337
- el.cropWindow,
338
- { borderRadius: windowRadius }
339
- ],
340
- children: !hideGrid && [1, 2].map((n) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
341
- /* @__PURE__ */ jsx(View, { style: [
342
- {
343
- position: "absolute",
344
- top: 0,
345
- bottom: 0,
346
- width: 1,
347
- left: crop.width * n / 3
348
- },
349
- el.gridLine
350
- ] }),
351
- /* @__PURE__ */ jsx(View, { style: [
352
- {
353
- position: "absolute",
354
- left: 0,
355
- right: 0,
356
- height: 1,
357
- top: crop.height * n / 3
358
- },
359
- el.gridLine
360
- ] })
361
- ] }, n))
362
- }
363
- ),
364
- /* @__PURE__ */ jsx(
365
- View,
366
- {
367
- pointerEvents: "none",
368
- style: [{ position: "absolute", right: 8, bottom: 8 }, el.zoomChip],
369
- children: /* @__PURE__ */ jsx(Text, { style: { color: el.zoomChip?.color }, children: zoomLabel })
370
- }
371
- )
372
- ] }),
373
- (loading || busy) && /* @__PURE__ */ jsx(View, { style: [{ position: "absolute", left: 0, right: 0, bottom: 0 }, el.progressFill] })
374
- ]
375
- }
376
- ) }),
377
- /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
378
- /* @__PURE__ */ jsx(Icon, { name: "zoom-in", size: "sm", color: el.icon?.color }),
379
- /* @__PURE__ */ jsx(
380
- Slider,
381
- {
382
- style: { flex: 1 },
383
- minimumValue: 0,
384
- maximumValue: 1,
385
- value: zoomFraction(state),
386
- disabled: !ready,
387
- minimumTrackTintColor: el.thumb?.backgroundColor,
388
- maximumTrackTintColor: el.track?.backgroundColor,
389
- thumbTintColor: el.thumb?.backgroundColor,
390
- onValueChange: (value) => dispatch({ type: "setZoom", value })
391
- }
392
- ),
393
- !hidePreview && ready && image && oriented && /* The preview is the same image, clipped and offset so the
394
- crop fills the frame — no pixel work, and therefore
395
- nothing that has to wait for an encoder. */
396
- /* @__PURE__ */ jsx(
397
- View,
482
+ children: [
483
+ /* @__PURE__ */ jsx(
484
+ ActivityIndicator,
485
+ {
486
+ size: "large",
487
+ color: el.progressFill?.backgroundColor ?? CROP_ACCENT
488
+ }
489
+ ),
490
+ busy && /* @__PURE__ */ jsx(Text, { style: { fontSize: 14, fontWeight: "700", color: "#FFFFFF" }, children: text.processing })
491
+ ]
492
+ }
493
+ )
494
+ ]
495
+ }
496
+ ) }),
497
+ /* @__PURE__ */ jsxs(
498
+ View,
499
+ {
500
+ pointerEvents: ready ? "auto" : "none",
501
+ style: {
502
+ paddingTop: 16,
503
+ paddingLeft: 18,
504
+ paddingRight: 18,
505
+ gap: 16,
506
+ opacity: ready ? 1 : 0.4
507
+ },
508
+ children: [
509
+ /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
510
+ !hidePreview && ready && image && oriented && /* The preview is the same image, clipped and offset so the
511
+ crop fills the frame — no pixel work, and therefore
512
+ nothing that has to wait for an encoder. */
513
+ /* @__PURE__ */ jsx(
514
+ View,
515
+ {
516
+ style: [
517
+ { width: previewWidth, height: previewHeight, overflow: "hidden" },
518
+ el.previewFrame,
519
+ isCircle ? { borderRadius: previewWidth / 2 } : null,
520
+ /* ⚠️ A 2px RING, NOT THE CARD'S 4. The slot
521
+ carries the desktop preview's ring; at 60pt
522
+ a 4pt ring eats the thumbnail. */
523
+ { borderWidth: 2 },
524
+ /* The mockup's float, in the native spelling —
525
+ `boxShadow` is the web file's half of this. */
526
+ {
527
+ shadowColor: "#000000",
528
+ shadowOpacity: 0.14,
529
+ shadowRadius: 14,
530
+ shadowOffset: { width: 0, height: 4 },
531
+ elevation: 4
532
+ }
533
+ ],
534
+ children: /* @__PURE__ */ jsx(
535
+ Image,
536
+ {
537
+ source: { uri: image.uri },
538
+ resizeMode: "stretch",
539
+ style: {
540
+ position: "absolute",
541
+ width: oriented.width * transform.scale * (previewWidth / crop.width),
542
+ height: oriented.height * transform.scale * (previewWidth / crop.width),
543
+ left: (transform.x - crop.x) * (previewWidth / crop.width),
544
+ top: (transform.y - crop.y) * (previewWidth / crop.width),
545
+ transform: [{ rotate: `${rotation}deg` }]
546
+ }
547
+ }
548
+ )
549
+ }
550
+ ),
551
+ /* @__PURE__ */ jsxs(View, { style: { flex: 1, alignItems: "flex-end" }, children: [
552
+ /* @__PURE__ */ jsx(Text, { style: { fontSize: 17, fontWeight: "800", color: "#FFFFFF" }, children: zoomLabel }),
553
+ /* @__PURE__ */ jsx(Text, { style: { marginTop: 2, fontSize: 11.5, color: "rgba(255,255,255,0.6)" }, children: `Under ${budgetKb} KB` })
554
+ ] })
555
+ ] }),
556
+ /* @__PURE__ */ jsxs(View, { style: { flexDirection: "row", alignItems: "center", gap: 10 }, children: [
557
+ stageTool("rotate-cw", text.rotate, () => dispatch({ type: "rotate" })),
558
+ /* @__PURE__ */ jsx(
559
+ Slider,
560
+ {
561
+ style: { flex: 1 },
562
+ minimumValue: 0,
563
+ maximumValue: 1,
564
+ value: zoomFraction(state),
565
+ disabled: !ready,
566
+ minimumTrackTintColor: el.thumb?.backgroundColor,
567
+ maximumTrackTintColor: el.track?.backgroundColor,
568
+ thumbTintColor: el.thumb?.backgroundColor,
569
+ onValueChange: (value) => dispatch({ type: "setZoom", value })
570
+ }
571
+ ),
572
+ stageTool("rotate-ccw", text.reset, () => dispatch({ type: "reset" }))
573
+ ] })
574
+ ]
575
+ }
576
+ )
577
+ ] }),
578
+ /* @__PURE__ */ jsxs(View, { style: [
579
+ { flexDirection: "row", alignItems: "center", marginTop: "auto" },
580
+ el.footer,
398
581
  {
399
- style: [
400
- { width: 64, height: 64, overflow: "hidden" },
401
- el.previewFrame,
402
- isCircle ? { borderRadius: 32 } : null
403
- ],
404
- children: /* @__PURE__ */ jsx(
405
- Image,
406
- {
407
- source: { uri: image.uri },
408
- resizeMode: "stretch",
409
- style: {
410
- position: "absolute",
411
- width: oriented.width * transform.scale * (64 / crop.width),
412
- height: oriented.height * transform.scale * (64 / crop.width),
413
- left: (transform.x - crop.x) * (64 / crop.width),
414
- top: (transform.y - crop.y) * (64 / crop.width),
415
- transform: [{ rotate: `${rotation}deg` }]
416
- }
417
- }
418
- )
582
+ paddingLeft: 18,
583
+ paddingRight: 18,
584
+ paddingTop: 12,
585
+ paddingBottom: 28,
586
+ borderTopWidth: 1,
587
+ borderTopColor: "rgba(255,255,255,0.08)",
588
+ gap: 12
419
589
  }
420
- )
421
- ] })
422
- ] }) }),
423
- /* @__PURE__ */ jsxs(View, { style: [{ flexDirection: "row", alignItems: "center" }, el.footer], children: [
424
- toolButton("rotate-cw", text.rotate, () => dispatch({ type: "rotate" }), !ready),
425
- toolButton("rotate-ccw", text.reset, () => dispatch({ type: "reset" }), !ready),
426
- /* @__PURE__ */ jsx(View, { style: { flex: 1 } }),
427
- toolButton("x", text.cancel, () => onCancel?.()),
428
- toolButton("check", busy ? text.processing : text.save, () => void save(), !canSave)
429
- ] })
430
- ] });
590
+ ], children: [
591
+ /* @__PURE__ */ jsx(
592
+ Pressable,
593
+ {
594
+ onPress: () => onCancel?.(),
595
+ accessibilityRole: "button",
596
+ accessibilityLabel: text.cancel,
597
+ style: {
598
+ flex: 1,
599
+ height: 52,
600
+ borderRadius: 26,
601
+ backgroundColor: "rgba(255,255,255,0.1)",
602
+ alignItems: "center",
603
+ justifyContent: "center"
604
+ },
605
+ children: /* @__PURE__ */ jsx(Text, { style: { fontSize: 15, fontWeight: "700", color: "#FFFFFF" }, children: text.cancel })
606
+ }
607
+ ),
608
+ /* @__PURE__ */ jsx(
609
+ Pressable,
610
+ {
611
+ onPress: () => void save(),
612
+ disabled: !canSave,
613
+ accessibilityRole: "button",
614
+ accessibilityLabel: text.save,
615
+ accessibilityState: { disabled: !canSave },
616
+ style: {
617
+ flex: 1.4,
618
+ height: 52,
619
+ borderRadius: 26,
620
+ backgroundColor: canSave ? CROP_ACCENT : "rgba(32,170,176,0.35)",
621
+ alignItems: "center",
622
+ justifyContent: "center"
623
+ },
624
+ children: /* @__PURE__ */ jsx(Text, { style: {
625
+ fontSize: 15,
626
+ fontWeight: "800",
627
+ color: canSave ? "#FFFFFF" : "rgba(255,255,255,0.55)"
628
+ }, children: busy ? text.processing : text.save })
629
+ }
630
+ )
631
+ ] })
632
+ ]
633
+ }
634
+ );
431
635
  }
432
636
 
433
637
  export { ImageCropper };