@ikatec/nebula-react 1.0.26 → 1.0.27
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.d.mts +24 -10
- package/dist/index.d.ts +24 -10
- package/dist/index.js +1140 -76
- package/dist/index.mjs +1136 -78
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -28,6 +28,7 @@ var dateFns = require('date-fns');
|
|
|
28
28
|
var reactDayPicker = require('react-day-picker');
|
|
29
29
|
var locale = require('react-day-picker/locale');
|
|
30
30
|
var mask = require('@react-input/mask');
|
|
31
|
+
var SliderPrimitive = require('@radix-ui/react-slider');
|
|
31
32
|
|
|
32
33
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
33
34
|
|
|
@@ -67,6 +68,7 @@ var AccordionPrimitive__namespace = /*#__PURE__*/_interopNamespace(AccordionPrim
|
|
|
67
68
|
var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
|
|
68
69
|
var RPNInput__namespace = /*#__PURE__*/_interopNamespace(RPNInput);
|
|
69
70
|
var flags__default = /*#__PURE__*/_interopDefault(flags);
|
|
71
|
+
var SliderPrimitive__namespace = /*#__PURE__*/_interopNamespace(SliderPrimitive);
|
|
70
72
|
|
|
71
73
|
// src/button.tsx
|
|
72
74
|
|
|
@@ -1157,8 +1159,16 @@ var messages5 = {
|
|
|
1157
1159
|
};
|
|
1158
1160
|
var file_upload_default = messages5;
|
|
1159
1161
|
|
|
1162
|
+
// src/i18n/messages/en/cropper.ts
|
|
1163
|
+
var messages6 = {
|
|
1164
|
+
applyButtonLabel: "Apply",
|
|
1165
|
+
dialogTitle: "Adjust image"
|
|
1166
|
+
};
|
|
1167
|
+
var cropper_default = messages6;
|
|
1168
|
+
|
|
1160
1169
|
// src/i18n/messages/en/index.ts
|
|
1161
1170
|
var enMessages = {
|
|
1171
|
+
cropper: cropper_default,
|
|
1162
1172
|
pagination: pagination_default,
|
|
1163
1173
|
inputSelect: input_select_default,
|
|
1164
1174
|
inputPhone: input_phone_default,
|
|
@@ -1167,7 +1177,7 @@ var enMessages = {
|
|
|
1167
1177
|
};
|
|
1168
1178
|
|
|
1169
1179
|
// src/i18n/messages/es/pagination.ts
|
|
1170
|
-
var
|
|
1180
|
+
var messages7 = {
|
|
1171
1181
|
totalResultsLabel(pagesSize, totalResults) {
|
|
1172
1182
|
if (totalResults < pagesSize) {
|
|
1173
1183
|
pagesSize = totalResults;
|
|
@@ -1178,16 +1188,16 @@ var messages6 = {
|
|
|
1178
1188
|
return `P\xE1gina ${currentPage} de ${totalPages}`;
|
|
1179
1189
|
}
|
|
1180
1190
|
};
|
|
1181
|
-
var pagination_default2 =
|
|
1191
|
+
var pagination_default2 = messages7;
|
|
1182
1192
|
|
|
1183
1193
|
// src/i18n/messages/es/input-select.ts
|
|
1184
|
-
var
|
|
1194
|
+
var messages8 = {
|
|
1185
1195
|
noOptions: "No hay opciones disponibles"
|
|
1186
1196
|
};
|
|
1187
|
-
var input_select_default2 =
|
|
1197
|
+
var input_select_default2 = messages8;
|
|
1188
1198
|
|
|
1189
1199
|
// src/i18n/messages/es/input-phone.ts
|
|
1190
|
-
var
|
|
1200
|
+
var messages9 = {
|
|
1191
1201
|
countries: {
|
|
1192
1202
|
empty: "Seleccionar",
|
|
1193
1203
|
AF: "Afganist\xE1n",
|
|
@@ -1437,22 +1447,30 @@ var messages8 = {
|
|
|
1437
1447
|
ZW: "Zimbabue"
|
|
1438
1448
|
}
|
|
1439
1449
|
};
|
|
1440
|
-
var input_phone_default2 =
|
|
1450
|
+
var input_phone_default2 = messages9;
|
|
1441
1451
|
|
|
1442
1452
|
// src/i18n/messages/es/time-picker.ts
|
|
1443
|
-
var
|
|
1453
|
+
var messages10 = {
|
|
1444
1454
|
label: "Tiempo"
|
|
1445
1455
|
};
|
|
1446
|
-
var time_picker_default2 =
|
|
1456
|
+
var time_picker_default2 = messages10;
|
|
1447
1457
|
|
|
1448
1458
|
// src/i18n/messages/es/file-upload.ts
|
|
1449
|
-
var
|
|
1459
|
+
var messages11 = {
|
|
1450
1460
|
deleteAll: "Remover todos"
|
|
1451
1461
|
};
|
|
1452
|
-
var file_upload_default2 =
|
|
1462
|
+
var file_upload_default2 = messages11;
|
|
1463
|
+
|
|
1464
|
+
// src/i18n/messages/es/cropper.ts
|
|
1465
|
+
var messages12 = {
|
|
1466
|
+
applyButtonLabel: "Aplicar",
|
|
1467
|
+
dialogTitle: "Ajustar la imagen"
|
|
1468
|
+
};
|
|
1469
|
+
var cropper_default2 = messages12;
|
|
1453
1470
|
|
|
1454
1471
|
// src/i18n/messages/es/index.ts
|
|
1455
1472
|
var esMessages = {
|
|
1473
|
+
cropper: cropper_default2,
|
|
1456
1474
|
pagination: pagination_default2,
|
|
1457
1475
|
inputSelect: input_select_default2,
|
|
1458
1476
|
inputPhone: input_phone_default2,
|
|
@@ -1461,7 +1479,7 @@ var esMessages = {
|
|
|
1461
1479
|
};
|
|
1462
1480
|
|
|
1463
1481
|
// src/i18n/messages/pt-br/pagination.ts
|
|
1464
|
-
var
|
|
1482
|
+
var messages13 = {
|
|
1465
1483
|
totalResultsLabel(pagesSize, totalResults) {
|
|
1466
1484
|
if (totalResults < pagesSize) {
|
|
1467
1485
|
pagesSize = totalResults;
|
|
@@ -1472,16 +1490,16 @@ var messages11 = {
|
|
|
1472
1490
|
return `P\xE1gina ${currentPage} de ${totalPages}`;
|
|
1473
1491
|
}
|
|
1474
1492
|
};
|
|
1475
|
-
var pagination_default3 =
|
|
1493
|
+
var pagination_default3 = messages13;
|
|
1476
1494
|
|
|
1477
1495
|
// src/i18n/messages/pt-br/input-select.ts
|
|
1478
|
-
var
|
|
1496
|
+
var messages14 = {
|
|
1479
1497
|
noOptions: "Nenhuma op\xE7\xE3o dispon\xEDvel"
|
|
1480
1498
|
};
|
|
1481
|
-
var input_select_default3 =
|
|
1499
|
+
var input_select_default3 = messages14;
|
|
1482
1500
|
|
|
1483
1501
|
// src/i18n/messages/pt-br/input-phone.ts
|
|
1484
|
-
var
|
|
1502
|
+
var messages15 = {
|
|
1485
1503
|
countries: {
|
|
1486
1504
|
empty: "Selecione",
|
|
1487
1505
|
AF: "Afeganist\xE3o",
|
|
@@ -1731,19 +1749,26 @@ var messages13 = {
|
|
|
1731
1749
|
ZW: "Zimb\xE1bue"
|
|
1732
1750
|
}
|
|
1733
1751
|
};
|
|
1734
|
-
var input_phone_default3 =
|
|
1752
|
+
var input_phone_default3 = messages15;
|
|
1735
1753
|
|
|
1736
1754
|
// src/i18n/messages/pt-br/time-picker.ts
|
|
1737
|
-
var
|
|
1755
|
+
var messages16 = {
|
|
1738
1756
|
label: "Hor\xE1rio"
|
|
1739
1757
|
};
|
|
1740
|
-
var time_picker_default3 =
|
|
1758
|
+
var time_picker_default3 = messages16;
|
|
1741
1759
|
|
|
1742
1760
|
// src/i18n/messages/pt-br/file-upload.ts
|
|
1743
|
-
var
|
|
1761
|
+
var messages17 = {
|
|
1744
1762
|
deleteAll: "Remover todos"
|
|
1745
1763
|
};
|
|
1746
|
-
var file_upload_default3 =
|
|
1764
|
+
var file_upload_default3 = messages17;
|
|
1765
|
+
|
|
1766
|
+
// src/i18n/messages/pt-br/cropper.ts
|
|
1767
|
+
var messages18 = {
|
|
1768
|
+
applyButtonLabel: "Aplicar",
|
|
1769
|
+
dialogTitle: "Ajustar imagem"
|
|
1770
|
+
};
|
|
1771
|
+
var cropper_default3 = messages18;
|
|
1747
1772
|
|
|
1748
1773
|
// src/i18n/messages/pt-br/index.ts
|
|
1749
1774
|
var ptBrMessages = {
|
|
@@ -1751,7 +1776,8 @@ var ptBrMessages = {
|
|
|
1751
1776
|
inputSelect: input_select_default3,
|
|
1752
1777
|
inputPhone: input_phone_default3,
|
|
1753
1778
|
timePicker: time_picker_default3,
|
|
1754
|
-
fileUpload: file_upload_default3
|
|
1779
|
+
fileUpload: file_upload_default3,
|
|
1780
|
+
cropper: cropper_default3
|
|
1755
1781
|
};
|
|
1756
1782
|
|
|
1757
1783
|
// src/i18n/message-storage-handler.ts
|
|
@@ -1776,7 +1802,7 @@ var setNebulaLanguage = (language) => {
|
|
|
1776
1802
|
}
|
|
1777
1803
|
localStorage.setItem(getNebulaI18nStorageKey(), language);
|
|
1778
1804
|
};
|
|
1779
|
-
var
|
|
1805
|
+
var messages19 = /* @__PURE__ */ new Map([
|
|
1780
1806
|
[null, enMessages],
|
|
1781
1807
|
[void 0, enMessages],
|
|
1782
1808
|
["en-US", enMessages],
|
|
@@ -1794,14 +1820,14 @@ var NebulaI18nProvider = ({
|
|
|
1794
1820
|
() => customI18nStorageKey ?? localStorageKey,
|
|
1795
1821
|
[customI18nStorageKey]
|
|
1796
1822
|
);
|
|
1797
|
-
const [
|
|
1798
|
-
|
|
1823
|
+
const [messages20, setMessages] = React8.useState(
|
|
1824
|
+
messages19.get(getNebulaLanguage()) ?? messages19.get("en-US")
|
|
1799
1825
|
);
|
|
1800
1826
|
const handleStorageChange = React8.useCallback(
|
|
1801
1827
|
({ detail }) => {
|
|
1802
1828
|
if (detail.key === storageKey) {
|
|
1803
1829
|
setMessages(
|
|
1804
|
-
|
|
1830
|
+
messages19.get(detail.value) ?? messages19.get("en-US")
|
|
1805
1831
|
);
|
|
1806
1832
|
}
|
|
1807
1833
|
},
|
|
@@ -1845,7 +1871,7 @@ var NebulaI18nProvider = ({
|
|
|
1845
1871
|
NebulaI18nContext.Provider,
|
|
1846
1872
|
{
|
|
1847
1873
|
value: {
|
|
1848
|
-
messages:
|
|
1874
|
+
messages: messages20,
|
|
1849
1875
|
locale: getNebulaLanguage()
|
|
1850
1876
|
},
|
|
1851
1877
|
children
|
|
@@ -1867,7 +1893,7 @@ var Pagination = ({
|
|
|
1867
1893
|
onChangePage,
|
|
1868
1894
|
...props
|
|
1869
1895
|
}) => {
|
|
1870
|
-
const { messages:
|
|
1896
|
+
const { messages: messages20 } = useNebulaI18n();
|
|
1871
1897
|
const totalPages = React8.useMemo(() => {
|
|
1872
1898
|
return Math.ceil(total / (pageSize || 1));
|
|
1873
1899
|
}, [total, pageSize]);
|
|
@@ -1900,13 +1926,13 @@ var Pagination = ({
|
|
|
1900
1926
|
}, [totalPages, pageSize, total]);
|
|
1901
1927
|
const totalResultsLabel = React8.useMemo(() => {
|
|
1902
1928
|
if (page === totalPages) {
|
|
1903
|
-
return
|
|
1929
|
+
return messages20.pagination.totalResultsLabel(lastPageSize, total);
|
|
1904
1930
|
}
|
|
1905
|
-
return
|
|
1906
|
-
}, [
|
|
1931
|
+
return messages20.pagination.totalResultsLabel(pageSize, total);
|
|
1932
|
+
}, [messages20.pagination, pageSize, total, page, totalPages, lastPageSize]);
|
|
1907
1933
|
const currentPageLabel = React8.useMemo(
|
|
1908
|
-
() =>
|
|
1909
|
-
[
|
|
1934
|
+
() => messages20.pagination.currentPageLabel(normalizedPage, totalPages),
|
|
1935
|
+
[messages20.pagination, normalizedPage, totalPages]
|
|
1910
1936
|
);
|
|
1911
1937
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1912
1938
|
"nav",
|
|
@@ -2179,7 +2205,7 @@ var createStyledSelect = (BaseSelect, displayName) => {
|
|
|
2179
2205
|
isError = false,
|
|
2180
2206
|
...props
|
|
2181
2207
|
}) => {
|
|
2182
|
-
const { messages:
|
|
2208
|
+
const { messages: messages20 } = useNebulaI18n();
|
|
2183
2209
|
const customClassNames = React8.useMemo(() => {
|
|
2184
2210
|
return {
|
|
2185
2211
|
control: (props2) => controlStyles(props2, isError),
|
|
@@ -2226,7 +2252,7 @@ var createStyledSelect = (BaseSelect, displayName) => {
|
|
|
2226
2252
|
isDisabled: disabled,
|
|
2227
2253
|
components: customComponents,
|
|
2228
2254
|
classNames: customClassNames,
|
|
2229
|
-
noOptionsMessage: () => /* @__PURE__ */ jsxRuntime.jsx("p", { children:
|
|
2255
|
+
noOptionsMessage: () => /* @__PURE__ */ jsxRuntime.jsx("p", { children: messages20.inputSelect.noOptions }),
|
|
2230
2256
|
...props
|
|
2231
2257
|
}
|
|
2232
2258
|
);
|
|
@@ -2372,16 +2398,17 @@ var DialogOverlay = React8__namespace.forwardRef(({ className, ...props }, ref)
|
|
|
2372
2398
|
}
|
|
2373
2399
|
));
|
|
2374
2400
|
DialogOverlay.displayName = DialogPrimitive__namespace.Overlay.displayName;
|
|
2375
|
-
var DialogContent = React8__namespace.forwardRef(
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2401
|
+
var DialogContent = React8__namespace.forwardRef(
|
|
2402
|
+
({ className, children, portal = false, showClose = true, ...props }, ref) => {
|
|
2403
|
+
const Comp = portal ? DialogPortal : React8__namespace.Fragment;
|
|
2404
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Comp, { children: [
|
|
2405
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogOverlay, {}),
|
|
2406
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2407
|
+
DialogPrimitive__namespace.Content,
|
|
2408
|
+
{
|
|
2409
|
+
ref,
|
|
2410
|
+
className: cn(
|
|
2411
|
+
`rounded-2xl
|
|
2385
2412
|
fixed
|
|
2386
2413
|
left-[50%]
|
|
2387
2414
|
top-[50%]
|
|
@@ -2406,29 +2433,30 @@ var DialogContent = React8__namespace.forwardRef(({ className, children, portal
|
|
|
2406
2433
|
data-[state=closed]:slide-out-to-top-[48%]
|
|
2407
2434
|
data-[state=open]:slide-in-from-left-1/2
|
|
2408
2435
|
data-[state=open]:slide-in-from-top-[48%]`,
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
}
|
|
2436
|
+
className
|
|
2437
|
+
),
|
|
2438
|
+
...props,
|
|
2439
|
+
children: [
|
|
2440
|
+
children,
|
|
2441
|
+
showClose && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2442
|
+
DialogPrimitive__namespace.Close,
|
|
2443
|
+
{
|
|
2444
|
+
asChild: true,
|
|
2445
|
+
className: `absolute
|
|
2446
|
+
right-4
|
|
2447
|
+
top-4`,
|
|
2448
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "ghost", size: "sm", icon: true, children: [
|
|
2449
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "nebula-ds !h-4 !w-4 !text-dialog-icon" }),
|
|
2450
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "nebula-ds sr-only", children: "Close" })
|
|
2451
|
+
] })
|
|
2452
|
+
}
|
|
2453
|
+
)
|
|
2454
|
+
]
|
|
2455
|
+
}
|
|
2456
|
+
)
|
|
2457
|
+
] });
|
|
2458
|
+
}
|
|
2459
|
+
);
|
|
2432
2460
|
DialogContent.displayName = DialogPrimitive__namespace.Content.displayName;
|
|
2433
2461
|
var DialogHeader = ({
|
|
2434
2462
|
className,
|
|
@@ -3244,13 +3272,13 @@ function custom(message, options) {
|
|
|
3244
3272
|
}
|
|
3245
3273
|
);
|
|
3246
3274
|
}
|
|
3247
|
-
async function promise(promise2,
|
|
3275
|
+
async function promise(promise2, messages20, options) {
|
|
3248
3276
|
const loadingToast = sonner.toast.custom(
|
|
3249
3277
|
(t) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3250
3278
|
ToastComponent,
|
|
3251
3279
|
{
|
|
3252
3280
|
type: "info",
|
|
3253
|
-
message:
|
|
3281
|
+
message: messages20.loading,
|
|
3254
3282
|
options,
|
|
3255
3283
|
t
|
|
3256
3284
|
}
|
|
@@ -3264,7 +3292,7 @@ async function promise(promise2, messages17, options) {
|
|
|
3264
3292
|
ToastComponent,
|
|
3265
3293
|
{
|
|
3266
3294
|
type: "success",
|
|
3267
|
-
message:
|
|
3295
|
+
message: messages20.success,
|
|
3268
3296
|
options,
|
|
3269
3297
|
t
|
|
3270
3298
|
}
|
|
@@ -3279,7 +3307,7 @@ async function promise(promise2, messages17, options) {
|
|
|
3279
3307
|
ToastComponent,
|
|
3280
3308
|
{
|
|
3281
3309
|
type: "error",
|
|
3282
|
-
message:
|
|
3310
|
+
message: messages20.error,
|
|
3283
3311
|
options,
|
|
3284
3312
|
t
|
|
3285
3313
|
}
|
|
@@ -3449,8 +3477,8 @@ var CountrySelect = ({
|
|
|
3449
3477
|
const handleSelect = (event) => {
|
|
3450
3478
|
onChange(event.target.value);
|
|
3451
3479
|
};
|
|
3452
|
-
const { messages:
|
|
3453
|
-
const { countries } =
|
|
3480
|
+
const { messages: messages20 } = useNebulaI18n();
|
|
3481
|
+
const { countries } = messages20.inputPhone;
|
|
3454
3482
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nebula-ds rounded-s-[20px] relative inline-flex items-center self-stretch py-2 ps-4 pe-2 transition-[color,box-shadow] outline-none has-disabled:pointer-events-none has-disabled:opacity-50", children: [
|
|
3455
3483
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nebula-ds inline-flex items-center gap-1", "aria-hidden": "true", children: [
|
|
3456
3484
|
/* @__PURE__ */ jsxRuntime.jsx(FlagComponent, { country: value, countryName: value, "aria-hidden": "true" }),
|
|
@@ -4270,7 +4298,7 @@ var InputDateTimePickerSingle = ({
|
|
|
4270
4298
|
formattedDateByLanguage ? new Date(formattedDateByLanguage) : void 0
|
|
4271
4299
|
);
|
|
4272
4300
|
const [popoverIsOpen, setPopoverIsOpen] = React8.useState(false);
|
|
4273
|
-
const { locale, messages:
|
|
4301
|
+
const { locale, messages: messages20 } = useNebulaI18n();
|
|
4274
4302
|
const [month, setMonth] = React8.useState(/* @__PURE__ */ new Date());
|
|
4275
4303
|
const inputTimeRef = React8.useRef(null);
|
|
4276
4304
|
const handleClearValue = () => {
|
|
@@ -4442,7 +4470,7 @@ var InputDateTimePickerSingle = ({
|
|
|
4442
4470
|
disabled: disabledDates,
|
|
4443
4471
|
footer: /* @__PURE__ */ jsxRuntime.jsxs(Space, { className: "nebula-ds items-center", children: [
|
|
4444
4472
|
/* @__PURE__ */ jsxRuntime.jsxs(Label, { children: [
|
|
4445
|
-
|
|
4473
|
+
messages20.timePicker.label,
|
|
4446
4474
|
":"
|
|
4447
4475
|
] }),
|
|
4448
4476
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -4984,9 +5012,11 @@ var ProfileImage = ({
|
|
|
4984
5012
|
onError,
|
|
4985
5013
|
maxFiles = Infinity,
|
|
4986
5014
|
onRemove,
|
|
5015
|
+
image,
|
|
4987
5016
|
...rest
|
|
4988
5017
|
}) => {
|
|
4989
5018
|
const maxSize = maxSizeMB * 1024 * 1024;
|
|
5019
|
+
const id = React8.useId();
|
|
4990
5020
|
const [
|
|
4991
5021
|
{ files, isDragging, errors },
|
|
4992
5022
|
{
|
|
@@ -4999,6 +5029,15 @@ var ProfileImage = ({
|
|
|
4999
5029
|
getInputProps
|
|
5000
5030
|
}
|
|
5001
5031
|
] = useFileUpload({
|
|
5032
|
+
initialFiles: image ? [
|
|
5033
|
+
{
|
|
5034
|
+
id,
|
|
5035
|
+
url: image,
|
|
5036
|
+
name: image,
|
|
5037
|
+
size: 0,
|
|
5038
|
+
type: "image"
|
|
5039
|
+
}
|
|
5040
|
+
] : [],
|
|
5002
5041
|
multiple: false,
|
|
5003
5042
|
maxSize: maxSize > 0 ? maxSize : void 0,
|
|
5004
5043
|
accept: "image/*",
|
|
@@ -5074,6 +5113,1026 @@ var ProfileImage = ({
|
|
|
5074
5113
|
] });
|
|
5075
5114
|
};
|
|
5076
5115
|
ProfileImage.displayName = "ProfileImage";
|
|
5116
|
+
function clamp(value, min, max) {
|
|
5117
|
+
return Math.min(Math.max(value, min), max);
|
|
5118
|
+
}
|
|
5119
|
+
var CropperContext = React8.createContext(null);
|
|
5120
|
+
var useCropperContext = () => {
|
|
5121
|
+
const context = React8.useContext(CropperContext);
|
|
5122
|
+
if (!context) {
|
|
5123
|
+
throw new Error("useCropperContext must be used within a Cropper.Root");
|
|
5124
|
+
}
|
|
5125
|
+
return context;
|
|
5126
|
+
};
|
|
5127
|
+
var CropperRoot = ({
|
|
5128
|
+
image,
|
|
5129
|
+
cropPadding = 25,
|
|
5130
|
+
aspectRatio = 1,
|
|
5131
|
+
minZoom = 1,
|
|
5132
|
+
maxZoom = 3,
|
|
5133
|
+
zoomSensitivity = 5e-3,
|
|
5134
|
+
keyboardStep = 10,
|
|
5135
|
+
className,
|
|
5136
|
+
style,
|
|
5137
|
+
zoom: zoomProp,
|
|
5138
|
+
onCropChange,
|
|
5139
|
+
onZoomChange,
|
|
5140
|
+
children,
|
|
5141
|
+
...restProps
|
|
5142
|
+
}) => {
|
|
5143
|
+
const descriptionId = React8.useId();
|
|
5144
|
+
const [imgWidth, setImgWidth] = React8.useState(null);
|
|
5145
|
+
const [imgHeight, setImgHeight] = React8.useState(null);
|
|
5146
|
+
const containerRef = React8.useRef(null);
|
|
5147
|
+
const [cropAreaWidth, setCropAreaWidth] = React8.useState(0);
|
|
5148
|
+
const [cropAreaHeight, setCropAreaHeight] = React8.useState(0);
|
|
5149
|
+
const [imageWrapperWidth, setImageWrapperWidth] = React8.useState(0);
|
|
5150
|
+
const [imageWrapperHeight, setImageWrapperHeight] = React8.useState(0);
|
|
5151
|
+
const [offsetX, setOffsetX] = React8.useState(0);
|
|
5152
|
+
const [offsetY, setOffsetY] = React8.useState(0);
|
|
5153
|
+
const [internalZoom, setInternalZoom] = React8.useState(minZoom);
|
|
5154
|
+
const [isDragging, setIsDragging] = React8.useState(false);
|
|
5155
|
+
const dragStartPointRef = React8.useRef({ x: 0, y: 0 });
|
|
5156
|
+
const dragStartOffsetRef = React8.useRef({ x: 0, y: 0 });
|
|
5157
|
+
const latestRestrictedOffsetRef = React8.useRef({
|
|
5158
|
+
x: offsetX,
|
|
5159
|
+
y: offsetY
|
|
5160
|
+
});
|
|
5161
|
+
const latestZoomRef = React8.useRef(internalZoom);
|
|
5162
|
+
const isInitialSetupDoneRef = React8.useRef(false);
|
|
5163
|
+
const initialPinchDistanceRef = React8.useRef(0);
|
|
5164
|
+
const initialPinchZoomRef = React8.useRef(1);
|
|
5165
|
+
const isPinchingRef = React8.useRef(false);
|
|
5166
|
+
const hasWarnedRef = React8.useRef(false);
|
|
5167
|
+
const isZoomControlled = zoomProp !== void 0;
|
|
5168
|
+
const effectiveZoom = isZoomControlled ? zoomProp : internalZoom;
|
|
5169
|
+
const updateZoom = React8.useCallback(
|
|
5170
|
+
(newZoomValue) => {
|
|
5171
|
+
const clampedZoom = clamp(newZoomValue, minZoom, maxZoom);
|
|
5172
|
+
if (onZoomChange) {
|
|
5173
|
+
onZoomChange(clampedZoom);
|
|
5174
|
+
} else if (!isZoomControlled) {
|
|
5175
|
+
setInternalZoom(clampedZoom);
|
|
5176
|
+
}
|
|
5177
|
+
return clampedZoom;
|
|
5178
|
+
},
|
|
5179
|
+
[minZoom, maxZoom, onZoomChange, isZoomControlled]
|
|
5180
|
+
);
|
|
5181
|
+
React8.useEffect(() => {
|
|
5182
|
+
latestZoomRef.current = effectiveZoom;
|
|
5183
|
+
}, [effectiveZoom]);
|
|
5184
|
+
React8.useEffect(() => {
|
|
5185
|
+
setOffsetX(0);
|
|
5186
|
+
setOffsetY(0);
|
|
5187
|
+
if (!isZoomControlled) {
|
|
5188
|
+
setInternalZoom(minZoom);
|
|
5189
|
+
}
|
|
5190
|
+
isInitialSetupDoneRef.current = false;
|
|
5191
|
+
if (!image) {
|
|
5192
|
+
setImgWidth(null);
|
|
5193
|
+
setImgHeight(null);
|
|
5194
|
+
return;
|
|
5195
|
+
}
|
|
5196
|
+
let isMounted = true;
|
|
5197
|
+
const img = new Image();
|
|
5198
|
+
img.onload = () => {
|
|
5199
|
+
if (isMounted) {
|
|
5200
|
+
setImgWidth(img.naturalWidth);
|
|
5201
|
+
setImgHeight(img.naturalHeight);
|
|
5202
|
+
}
|
|
5203
|
+
};
|
|
5204
|
+
img.onerror = () => {
|
|
5205
|
+
if (isMounted) {
|
|
5206
|
+
setImgWidth(null);
|
|
5207
|
+
setImgHeight(null);
|
|
5208
|
+
}
|
|
5209
|
+
};
|
|
5210
|
+
img.src = image;
|
|
5211
|
+
return () => {
|
|
5212
|
+
isMounted = false;
|
|
5213
|
+
};
|
|
5214
|
+
}, [image, minZoom, isZoomControlled]);
|
|
5215
|
+
const updateCropAreaDimensions = React8.useCallback(
|
|
5216
|
+
(containerWidth, containerHeight) => {
|
|
5217
|
+
if (containerWidth <= 0 || containerHeight <= 0) {
|
|
5218
|
+
setCropAreaWidth(0);
|
|
5219
|
+
setCropAreaHeight(0);
|
|
5220
|
+
return;
|
|
5221
|
+
}
|
|
5222
|
+
const maxPossibleWidth = Math.max(0, containerWidth - cropPadding * 2);
|
|
5223
|
+
const maxPossibleHeight = Math.max(0, containerHeight - cropPadding * 2);
|
|
5224
|
+
let targetCropW = 0;
|
|
5225
|
+
let targetCropH = 0;
|
|
5226
|
+
if (maxPossibleWidth / aspectRatio >= maxPossibleHeight) {
|
|
5227
|
+
targetCropH = maxPossibleHeight;
|
|
5228
|
+
targetCropW = targetCropH * aspectRatio;
|
|
5229
|
+
} else {
|
|
5230
|
+
targetCropW = maxPossibleWidth;
|
|
5231
|
+
targetCropH = targetCropW / aspectRatio;
|
|
5232
|
+
}
|
|
5233
|
+
setCropAreaWidth(targetCropW);
|
|
5234
|
+
setCropAreaHeight(targetCropH);
|
|
5235
|
+
},
|
|
5236
|
+
[aspectRatio, cropPadding]
|
|
5237
|
+
);
|
|
5238
|
+
React8.useEffect(() => {
|
|
5239
|
+
const element = containerRef.current;
|
|
5240
|
+
if (!element) return;
|
|
5241
|
+
const observer = new ResizeObserver((entries) => {
|
|
5242
|
+
for (const entry of entries) {
|
|
5243
|
+
const { width, height } = entry.contentRect;
|
|
5244
|
+
if (width > 0 && height > 0) updateCropAreaDimensions(width, height);
|
|
5245
|
+
}
|
|
5246
|
+
});
|
|
5247
|
+
observer.observe(element);
|
|
5248
|
+
const initialWidth = element.clientWidth;
|
|
5249
|
+
const initialHeight = element.clientHeight;
|
|
5250
|
+
if (initialWidth > 0 && initialHeight > 0)
|
|
5251
|
+
updateCropAreaDimensions(initialWidth, initialHeight);
|
|
5252
|
+
return () => observer.disconnect();
|
|
5253
|
+
}, [updateCropAreaDimensions]);
|
|
5254
|
+
React8.useEffect(() => {
|
|
5255
|
+
if (cropAreaWidth <= 0 || cropAreaHeight <= 0 || !imgWidth || !imgHeight) {
|
|
5256
|
+
setImageWrapperWidth(0);
|
|
5257
|
+
setImageWrapperHeight(0);
|
|
5258
|
+
return;
|
|
5259
|
+
}
|
|
5260
|
+
const naturalAspect = imgWidth / imgHeight;
|
|
5261
|
+
const cropAspect = cropAreaWidth / cropAreaHeight;
|
|
5262
|
+
let targetWrapperWidth = 0;
|
|
5263
|
+
let targetWrapperHeight = 0;
|
|
5264
|
+
if (naturalAspect >= cropAspect) {
|
|
5265
|
+
targetWrapperHeight = cropAreaHeight;
|
|
5266
|
+
targetWrapperWidth = targetWrapperHeight * naturalAspect;
|
|
5267
|
+
} else {
|
|
5268
|
+
targetWrapperWidth = cropAreaWidth;
|
|
5269
|
+
targetWrapperHeight = targetWrapperWidth / naturalAspect;
|
|
5270
|
+
}
|
|
5271
|
+
setImageWrapperWidth(targetWrapperWidth);
|
|
5272
|
+
setImageWrapperHeight(targetWrapperHeight);
|
|
5273
|
+
}, [cropAreaWidth, cropAreaHeight, imgWidth, imgHeight]);
|
|
5274
|
+
const restrictOffset = React8.useCallback(
|
|
5275
|
+
(dragOffsetX, dragOffsetY, currentZoom) => {
|
|
5276
|
+
if (imageWrapperWidth <= 0 || imageWrapperHeight <= 0 || cropAreaWidth <= 0 || cropAreaHeight <= 0)
|
|
5277
|
+
return { x: 0, y: 0 };
|
|
5278
|
+
const effectiveWrapperWidth = imageWrapperWidth * currentZoom;
|
|
5279
|
+
const effectiveWrapperHeight = imageWrapperHeight * currentZoom;
|
|
5280
|
+
const maxDragX = Math.max(0, (effectiveWrapperWidth - cropAreaWidth) / 2);
|
|
5281
|
+
const maxDragY = Math.max(
|
|
5282
|
+
0,
|
|
5283
|
+
(effectiveWrapperHeight - cropAreaHeight) / 2
|
|
5284
|
+
);
|
|
5285
|
+
return {
|
|
5286
|
+
x: clamp(dragOffsetX, -maxDragX, maxDragX),
|
|
5287
|
+
y: clamp(dragOffsetY, -maxDragY, maxDragY)
|
|
5288
|
+
};
|
|
5289
|
+
},
|
|
5290
|
+
[imageWrapperWidth, imageWrapperHeight, cropAreaWidth, cropAreaHeight]
|
|
5291
|
+
);
|
|
5292
|
+
const calculateCropData = React8.useCallback(
|
|
5293
|
+
(finalOffsetX, finalOffsetY, finalZoom) => {
|
|
5294
|
+
const currentOffsetX = finalOffsetX !== void 0 ? finalOffsetX : latestRestrictedOffsetRef.current.x;
|
|
5295
|
+
const currentOffsetY = finalOffsetY !== void 0 ? finalOffsetY : latestRestrictedOffsetRef.current.y;
|
|
5296
|
+
const currentZoom = finalZoom !== void 0 ? finalZoom : effectiveZoom;
|
|
5297
|
+
if (!imgWidth || !imgHeight || imageWrapperWidth <= 0 || imageWrapperHeight <= 0 || cropAreaWidth <= 0 || cropAreaHeight <= 0)
|
|
5298
|
+
return null;
|
|
5299
|
+
const scaledWrapperWidth = imageWrapperWidth * currentZoom;
|
|
5300
|
+
const scaledWrapperHeight = imageWrapperHeight * currentZoom;
|
|
5301
|
+
const topLeftOffsetX = currentOffsetX + (cropAreaWidth - scaledWrapperWidth) / 2;
|
|
5302
|
+
const topLeftOffsetY = currentOffsetY + (cropAreaHeight - scaledWrapperHeight) / 2;
|
|
5303
|
+
const baseScale = imgWidth / imageWrapperWidth;
|
|
5304
|
+
if (isNaN(baseScale) || baseScale === 0) return null;
|
|
5305
|
+
const sx = -topLeftOffsetX * baseScale / currentZoom;
|
|
5306
|
+
const sy = -topLeftOffsetY * baseScale / currentZoom;
|
|
5307
|
+
const sWidth = cropAreaWidth * baseScale / currentZoom;
|
|
5308
|
+
const sHeight = cropAreaHeight * baseScale / currentZoom;
|
|
5309
|
+
const finalX = clamp(Math.round(sx), 0, imgWidth);
|
|
5310
|
+
const finalY = clamp(Math.round(sy), 0, imgHeight);
|
|
5311
|
+
const finalWidth = clamp(Math.round(sWidth), 0, imgWidth - finalX);
|
|
5312
|
+
const finalHeight = clamp(Math.round(sHeight), 0, imgHeight - finalY);
|
|
5313
|
+
if (finalWidth <= 0 || finalHeight <= 0) return null;
|
|
5314
|
+
return { x: finalX, y: finalY, width: finalWidth, height: finalHeight };
|
|
5315
|
+
},
|
|
5316
|
+
[
|
|
5317
|
+
imgWidth,
|
|
5318
|
+
imgHeight,
|
|
5319
|
+
imageWrapperWidth,
|
|
5320
|
+
imageWrapperHeight,
|
|
5321
|
+
cropAreaWidth,
|
|
5322
|
+
cropAreaHeight,
|
|
5323
|
+
effectiveZoom
|
|
5324
|
+
]
|
|
5325
|
+
);
|
|
5326
|
+
React8.useEffect(() => {
|
|
5327
|
+
if (imageWrapperWidth > 0 && imageWrapperHeight > 0 && cropAreaWidth > 0 && cropAreaHeight > 0) {
|
|
5328
|
+
const currentZoomForSetup = effectiveZoom;
|
|
5329
|
+
if (!isInitialSetupDoneRef.current) {
|
|
5330
|
+
const initialX = 0;
|
|
5331
|
+
const initialY = 0;
|
|
5332
|
+
const restrictedInitial = restrictOffset(
|
|
5333
|
+
initialX,
|
|
5334
|
+
initialY,
|
|
5335
|
+
currentZoomForSetup
|
|
5336
|
+
);
|
|
5337
|
+
setOffsetX(restrictedInitial.x);
|
|
5338
|
+
setOffsetY(restrictedInitial.y);
|
|
5339
|
+
if (!isZoomControlled) setInternalZoom(currentZoomForSetup);
|
|
5340
|
+
dragStartOffsetRef.current = restrictedInitial;
|
|
5341
|
+
latestRestrictedOffsetRef.current = restrictedInitial;
|
|
5342
|
+
latestZoomRef.current = currentZoomForSetup;
|
|
5343
|
+
if (onCropChange)
|
|
5344
|
+
onCropChange(
|
|
5345
|
+
calculateCropData(
|
|
5346
|
+
restrictedInitial.x,
|
|
5347
|
+
restrictedInitial.y,
|
|
5348
|
+
currentZoomForSetup
|
|
5349
|
+
)
|
|
5350
|
+
);
|
|
5351
|
+
isInitialSetupDoneRef.current = true;
|
|
5352
|
+
} else {
|
|
5353
|
+
const restrictedCurrent = restrictOffset(
|
|
5354
|
+
latestRestrictedOffsetRef.current.x,
|
|
5355
|
+
latestRestrictedOffsetRef.current.y,
|
|
5356
|
+
currentZoomForSetup
|
|
5357
|
+
);
|
|
5358
|
+
if (restrictedCurrent.x !== latestRestrictedOffsetRef.current.x || restrictedCurrent.y !== latestRestrictedOffsetRef.current.y) {
|
|
5359
|
+
setOffsetX(restrictedCurrent.x);
|
|
5360
|
+
setOffsetY(restrictedCurrent.y);
|
|
5361
|
+
latestRestrictedOffsetRef.current = restrictedCurrent;
|
|
5362
|
+
dragStartOffsetRef.current = restrictedCurrent;
|
|
5363
|
+
}
|
|
5364
|
+
if (onCropChange)
|
|
5365
|
+
onCropChange(
|
|
5366
|
+
calculateCropData(
|
|
5367
|
+
restrictedCurrent.x,
|
|
5368
|
+
restrictedCurrent.y,
|
|
5369
|
+
currentZoomForSetup
|
|
5370
|
+
)
|
|
5371
|
+
);
|
|
5372
|
+
}
|
|
5373
|
+
} else {
|
|
5374
|
+
isInitialSetupDoneRef.current = false;
|
|
5375
|
+
setOffsetX(0);
|
|
5376
|
+
setOffsetY(0);
|
|
5377
|
+
if (!isZoomControlled) setInternalZoom(minZoom);
|
|
5378
|
+
dragStartOffsetRef.current = { x: 0, y: 0 };
|
|
5379
|
+
latestRestrictedOffsetRef.current = { x: 0, y: 0 };
|
|
5380
|
+
latestZoomRef.current = effectiveZoom;
|
|
5381
|
+
if (onCropChange) onCropChange(null);
|
|
5382
|
+
}
|
|
5383
|
+
}, [
|
|
5384
|
+
imageWrapperWidth,
|
|
5385
|
+
imgHeight,
|
|
5386
|
+
cropAreaWidth,
|
|
5387
|
+
cropAreaHeight,
|
|
5388
|
+
restrictOffset,
|
|
5389
|
+
onCropChange,
|
|
5390
|
+
calculateCropData,
|
|
5391
|
+
minZoom,
|
|
5392
|
+
effectiveZoom,
|
|
5393
|
+
isZoomControlled,
|
|
5394
|
+
updateZoom,
|
|
5395
|
+
imageWrapperHeight
|
|
5396
|
+
]);
|
|
5397
|
+
React8.useEffect(() => {
|
|
5398
|
+
const checkTimeout = setTimeout(() => {
|
|
5399
|
+
if (containerRef.current && !hasWarnedRef.current) {
|
|
5400
|
+
const hasDescription = document.getElementById(descriptionId);
|
|
5401
|
+
if (!hasDescription) {
|
|
5402
|
+
hasWarnedRef.current = true;
|
|
5403
|
+
}
|
|
5404
|
+
}
|
|
5405
|
+
}, 100);
|
|
5406
|
+
return () => clearTimeout(checkTimeout);
|
|
5407
|
+
}, [descriptionId]);
|
|
5408
|
+
const handleInteractionEnd = React8.useCallback(() => {
|
|
5409
|
+
if (onCropChange) {
|
|
5410
|
+
const finalData = calculateCropData(
|
|
5411
|
+
latestRestrictedOffsetRef.current.x,
|
|
5412
|
+
latestRestrictedOffsetRef.current.y,
|
|
5413
|
+
effectiveZoom
|
|
5414
|
+
);
|
|
5415
|
+
onCropChange(finalData);
|
|
5416
|
+
}
|
|
5417
|
+
}, [onCropChange, calculateCropData, effectiveZoom]);
|
|
5418
|
+
const handleMouseDown = React8.useCallback(
|
|
5419
|
+
(e) => {
|
|
5420
|
+
if (e.button !== 0 || !containerRef.current) return;
|
|
5421
|
+
e.preventDefault();
|
|
5422
|
+
setIsDragging(true);
|
|
5423
|
+
isPinchingRef.current = false;
|
|
5424
|
+
dragStartPointRef.current = { x: e.clientX, y: e.clientY };
|
|
5425
|
+
dragStartOffsetRef.current = {
|
|
5426
|
+
x: latestRestrictedOffsetRef.current.x,
|
|
5427
|
+
y: latestRestrictedOffsetRef.current.y
|
|
5428
|
+
};
|
|
5429
|
+
const handleMouseMove = (ev) => {
|
|
5430
|
+
const deltaX = ev.clientX - dragStartPointRef.current.x;
|
|
5431
|
+
const deltaY = ev.clientY - dragStartPointRef.current.y;
|
|
5432
|
+
const targetOffsetX = dragStartOffsetRef.current.x + deltaX;
|
|
5433
|
+
const targetOffsetY = dragStartOffsetRef.current.y + deltaY;
|
|
5434
|
+
const restricted = restrictOffset(
|
|
5435
|
+
targetOffsetX,
|
|
5436
|
+
targetOffsetY,
|
|
5437
|
+
effectiveZoom
|
|
5438
|
+
);
|
|
5439
|
+
latestRestrictedOffsetRef.current = restricted;
|
|
5440
|
+
setOffsetX(restricted.x);
|
|
5441
|
+
setOffsetY(restricted.y);
|
|
5442
|
+
};
|
|
5443
|
+
const handleMouseUp = () => {
|
|
5444
|
+
setIsDragging(false);
|
|
5445
|
+
window.removeEventListener("mousemove", handleMouseMove);
|
|
5446
|
+
window.removeEventListener("mouseup", handleMouseUp);
|
|
5447
|
+
handleInteractionEnd();
|
|
5448
|
+
};
|
|
5449
|
+
window.addEventListener("mousemove", handleMouseMove);
|
|
5450
|
+
window.addEventListener("mouseup", handleMouseUp);
|
|
5451
|
+
},
|
|
5452
|
+
[restrictOffset, effectiveZoom, handleInteractionEnd]
|
|
5453
|
+
);
|
|
5454
|
+
const handleWheel = React8.useCallback(
|
|
5455
|
+
(e) => {
|
|
5456
|
+
e.preventDefault();
|
|
5457
|
+
e.stopPropagation();
|
|
5458
|
+
if (!containerRef.current || imageWrapperWidth <= 0 || imageWrapperHeight <= 0)
|
|
5459
|
+
return;
|
|
5460
|
+
const currentZoom = latestZoomRef.current;
|
|
5461
|
+
const currentOffsetX = latestRestrictedOffsetRef.current.x;
|
|
5462
|
+
const currentOffsetY = latestRestrictedOffsetRef.current.y;
|
|
5463
|
+
const delta = e.deltaY * -zoomSensitivity;
|
|
5464
|
+
const targetZoom = currentZoom + delta;
|
|
5465
|
+
if (clamp(targetZoom, minZoom, maxZoom) === currentZoom) return;
|
|
5466
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
5467
|
+
const pointerX = e.clientX - rect.left - rect.width / 2;
|
|
5468
|
+
const pointerY = e.clientY - rect.top - rect.height / 2;
|
|
5469
|
+
const imagePointX = (pointerX - currentOffsetX) / currentZoom;
|
|
5470
|
+
const imagePointY = (pointerY - currentOffsetY) / currentZoom;
|
|
5471
|
+
const finalNewZoom = updateZoom(targetZoom);
|
|
5472
|
+
const newOffsetX = pointerX - imagePointX * finalNewZoom;
|
|
5473
|
+
const newOffsetY = pointerY - imagePointY * finalNewZoom;
|
|
5474
|
+
const restrictedNewOffset = restrictOffset(
|
|
5475
|
+
newOffsetX,
|
|
5476
|
+
newOffsetY,
|
|
5477
|
+
finalNewZoom
|
|
5478
|
+
);
|
|
5479
|
+
setOffsetX(restrictedNewOffset.x);
|
|
5480
|
+
setOffsetY(restrictedNewOffset.y);
|
|
5481
|
+
latestRestrictedOffsetRef.current = restrictedNewOffset;
|
|
5482
|
+
if (onCropChange) {
|
|
5483
|
+
const finalData = calculateCropData(
|
|
5484
|
+
restrictedNewOffset.x,
|
|
5485
|
+
restrictedNewOffset.y,
|
|
5486
|
+
finalNewZoom
|
|
5487
|
+
);
|
|
5488
|
+
onCropChange(finalData);
|
|
5489
|
+
}
|
|
5490
|
+
},
|
|
5491
|
+
[
|
|
5492
|
+
restrictOffset,
|
|
5493
|
+
calculateCropData,
|
|
5494
|
+
imageWrapperWidth,
|
|
5495
|
+
imageWrapperHeight,
|
|
5496
|
+
onCropChange,
|
|
5497
|
+
minZoom,
|
|
5498
|
+
maxZoom,
|
|
5499
|
+
zoomSensitivity,
|
|
5500
|
+
updateZoom
|
|
5501
|
+
]
|
|
5502
|
+
);
|
|
5503
|
+
const getPinchDistance = (touches) => Math.sqrt(
|
|
5504
|
+
Math.pow(touches[1].clientX - touches[0].clientX, 2) + Math.pow(touches[1].clientY - touches[0].clientY, 2)
|
|
5505
|
+
);
|
|
5506
|
+
const getPinchCenter = (touches) => ({
|
|
5507
|
+
x: (touches[0].clientX + touches[1].clientX) / 2,
|
|
5508
|
+
y: (touches[0].clientY + touches[1].clientY) / 2
|
|
5509
|
+
});
|
|
5510
|
+
const handleTouchStart = React8.useCallback(
|
|
5511
|
+
(e) => {
|
|
5512
|
+
if (!containerRef.current || imageWrapperWidth <= 0 || imageWrapperHeight <= 0)
|
|
5513
|
+
return;
|
|
5514
|
+
e.preventDefault();
|
|
5515
|
+
const touches = e.touches;
|
|
5516
|
+
if (touches.length === 1) {
|
|
5517
|
+
setIsDragging(true);
|
|
5518
|
+
isPinchingRef.current = false;
|
|
5519
|
+
dragStartPointRef.current = {
|
|
5520
|
+
x: touches[0].clientX,
|
|
5521
|
+
y: touches[0].clientY
|
|
5522
|
+
};
|
|
5523
|
+
dragStartOffsetRef.current = {
|
|
5524
|
+
x: latestRestrictedOffsetRef.current.x,
|
|
5525
|
+
y: latestRestrictedOffsetRef.current.y
|
|
5526
|
+
};
|
|
5527
|
+
} else if (touches.length === 2) {
|
|
5528
|
+
setIsDragging(false);
|
|
5529
|
+
isPinchingRef.current = true;
|
|
5530
|
+
initialPinchDistanceRef.current = getPinchDistance(touches);
|
|
5531
|
+
initialPinchZoomRef.current = latestZoomRef.current;
|
|
5532
|
+
dragStartOffsetRef.current = {
|
|
5533
|
+
x: latestRestrictedOffsetRef.current.x,
|
|
5534
|
+
y: latestRestrictedOffsetRef.current.y
|
|
5535
|
+
};
|
|
5536
|
+
}
|
|
5537
|
+
},
|
|
5538
|
+
[imageWrapperWidth, imageWrapperHeight]
|
|
5539
|
+
);
|
|
5540
|
+
const handleTouchMove = React8.useCallback(
|
|
5541
|
+
(e) => {
|
|
5542
|
+
if (!containerRef.current || imageWrapperWidth <= 0 || imageWrapperHeight <= 0)
|
|
5543
|
+
return;
|
|
5544
|
+
e.preventDefault();
|
|
5545
|
+
const touches = e.touches;
|
|
5546
|
+
if (touches.length === 1 && isDragging && !isPinchingRef.current) {
|
|
5547
|
+
const deltaX = touches[0].clientX - dragStartPointRef.current.x;
|
|
5548
|
+
const deltaY = touches[0].clientY - dragStartPointRef.current.y;
|
|
5549
|
+
const targetOffsetX = dragStartOffsetRef.current.x + deltaX;
|
|
5550
|
+
const targetOffsetY = dragStartOffsetRef.current.y + deltaY;
|
|
5551
|
+
const restricted = restrictOffset(
|
|
5552
|
+
targetOffsetX,
|
|
5553
|
+
targetOffsetY,
|
|
5554
|
+
effectiveZoom
|
|
5555
|
+
);
|
|
5556
|
+
latestRestrictedOffsetRef.current = restricted;
|
|
5557
|
+
setOffsetX(restricted.x);
|
|
5558
|
+
setOffsetY(restricted.y);
|
|
5559
|
+
} else if (touches.length === 2 && isPinchingRef.current) {
|
|
5560
|
+
const currentPinchDistance = getPinchDistance(touches);
|
|
5561
|
+
const scale = currentPinchDistance / initialPinchDistanceRef.current;
|
|
5562
|
+
const currentZoom = initialPinchZoomRef.current;
|
|
5563
|
+
const targetZoom = currentZoom * scale;
|
|
5564
|
+
if (clamp(targetZoom, minZoom, maxZoom) === latestZoomRef.current)
|
|
5565
|
+
return;
|
|
5566
|
+
const pinchCenter = getPinchCenter(touches);
|
|
5567
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
5568
|
+
const pinchCenterX = pinchCenter.x - rect.left - rect.width / 2;
|
|
5569
|
+
const pinchCenterY = pinchCenter.y - rect.top - rect.height / 2;
|
|
5570
|
+
const currentOffsetX = dragStartOffsetRef.current.x;
|
|
5571
|
+
const currentOffsetY = dragStartOffsetRef.current.y;
|
|
5572
|
+
const imagePointX = (pinchCenterX - currentOffsetX) / currentZoom;
|
|
5573
|
+
const imagePointY = (pinchCenterY - currentOffsetY) / currentZoom;
|
|
5574
|
+
const finalNewZoom = updateZoom(targetZoom);
|
|
5575
|
+
const newOffsetX = pinchCenterX - imagePointX * finalNewZoom;
|
|
5576
|
+
const newOffsetY = pinchCenterY - imagePointY * finalNewZoom;
|
|
5577
|
+
const restrictedNewOffset = restrictOffset(
|
|
5578
|
+
newOffsetX,
|
|
5579
|
+
newOffsetY,
|
|
5580
|
+
finalNewZoom
|
|
5581
|
+
);
|
|
5582
|
+
setOffsetX(restrictedNewOffset.x);
|
|
5583
|
+
setOffsetY(restrictedNewOffset.y);
|
|
5584
|
+
latestRestrictedOffsetRef.current = restrictedNewOffset;
|
|
5585
|
+
if (onCropChange) {
|
|
5586
|
+
const finalData = calculateCropData(
|
|
5587
|
+
restrictedNewOffset.x,
|
|
5588
|
+
restrictedNewOffset.y,
|
|
5589
|
+
finalNewZoom
|
|
5590
|
+
);
|
|
5591
|
+
onCropChange(finalData);
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
},
|
|
5595
|
+
[
|
|
5596
|
+
isDragging,
|
|
5597
|
+
restrictOffset,
|
|
5598
|
+
minZoom,
|
|
5599
|
+
maxZoom,
|
|
5600
|
+
imageWrapperWidth,
|
|
5601
|
+
imageWrapperHeight,
|
|
5602
|
+
effectiveZoom,
|
|
5603
|
+
updateZoom,
|
|
5604
|
+
onCropChange,
|
|
5605
|
+
calculateCropData
|
|
5606
|
+
]
|
|
5607
|
+
);
|
|
5608
|
+
const handleTouchEnd = React8.useCallback(
|
|
5609
|
+
(e) => {
|
|
5610
|
+
e.preventDefault();
|
|
5611
|
+
const touches = e.touches;
|
|
5612
|
+
if (isPinchingRef.current && touches.length < 2) {
|
|
5613
|
+
isPinchingRef.current = false;
|
|
5614
|
+
if (touches.length === 1) {
|
|
5615
|
+
setIsDragging(true);
|
|
5616
|
+
dragStartPointRef.current = {
|
|
5617
|
+
x: touches[0].clientX,
|
|
5618
|
+
y: touches[0].clientY
|
|
5619
|
+
};
|
|
5620
|
+
dragStartOffsetRef.current = {
|
|
5621
|
+
x: latestRestrictedOffsetRef.current.x,
|
|
5622
|
+
y: latestRestrictedOffsetRef.current.y
|
|
5623
|
+
};
|
|
5624
|
+
} else {
|
|
5625
|
+
setIsDragging(false);
|
|
5626
|
+
handleInteractionEnd();
|
|
5627
|
+
}
|
|
5628
|
+
} else if (isDragging && touches.length === 0) {
|
|
5629
|
+
setIsDragging(false);
|
|
5630
|
+
handleInteractionEnd();
|
|
5631
|
+
}
|
|
5632
|
+
},
|
|
5633
|
+
[isDragging, handleInteractionEnd]
|
|
5634
|
+
);
|
|
5635
|
+
const handleKeyDown = React8.useCallback(
|
|
5636
|
+
(e) => {
|
|
5637
|
+
if (imageWrapperWidth <= 0) return;
|
|
5638
|
+
let targetOffsetX = latestRestrictedOffsetRef.current.x;
|
|
5639
|
+
let targetOffsetY = latestRestrictedOffsetRef.current.y;
|
|
5640
|
+
let moved = false;
|
|
5641
|
+
switch (e.key) {
|
|
5642
|
+
case "ArrowUp":
|
|
5643
|
+
targetOffsetY += keyboardStep;
|
|
5644
|
+
moved = true;
|
|
5645
|
+
break;
|
|
5646
|
+
case "ArrowDown":
|
|
5647
|
+
targetOffsetY -= keyboardStep;
|
|
5648
|
+
moved = true;
|
|
5649
|
+
break;
|
|
5650
|
+
case "ArrowLeft":
|
|
5651
|
+
targetOffsetX += keyboardStep;
|
|
5652
|
+
moved = true;
|
|
5653
|
+
break;
|
|
5654
|
+
case "ArrowRight":
|
|
5655
|
+
targetOffsetX -= keyboardStep;
|
|
5656
|
+
moved = true;
|
|
5657
|
+
break;
|
|
5658
|
+
default:
|
|
5659
|
+
return;
|
|
5660
|
+
}
|
|
5661
|
+
if (moved) {
|
|
5662
|
+
e.preventDefault();
|
|
5663
|
+
const restricted = restrictOffset(
|
|
5664
|
+
targetOffsetX,
|
|
5665
|
+
targetOffsetY,
|
|
5666
|
+
effectiveZoom
|
|
5667
|
+
);
|
|
5668
|
+
if (restricted.x !== latestRestrictedOffsetRef.current.x || restricted.y !== latestRestrictedOffsetRef.current.y) {
|
|
5669
|
+
latestRestrictedOffsetRef.current = restricted;
|
|
5670
|
+
setOffsetX(restricted.x);
|
|
5671
|
+
setOffsetY(restricted.y);
|
|
5672
|
+
}
|
|
5673
|
+
}
|
|
5674
|
+
},
|
|
5675
|
+
[keyboardStep, imageWrapperWidth, restrictOffset, effectiveZoom]
|
|
5676
|
+
);
|
|
5677
|
+
const handleKeyUp = React8.useCallback(
|
|
5678
|
+
(e) => {
|
|
5679
|
+
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(e.key)) {
|
|
5680
|
+
handleInteractionEnd();
|
|
5681
|
+
}
|
|
5682
|
+
},
|
|
5683
|
+
[handleInteractionEnd]
|
|
5684
|
+
);
|
|
5685
|
+
React8.useEffect(() => {
|
|
5686
|
+
const node = containerRef.current;
|
|
5687
|
+
if (!node) return;
|
|
5688
|
+
const options = { passive: false };
|
|
5689
|
+
node.addEventListener("wheel", handleWheel, options);
|
|
5690
|
+
node.addEventListener("touchstart", handleTouchStart, options);
|
|
5691
|
+
node.addEventListener("touchmove", handleTouchMove, options);
|
|
5692
|
+
node.addEventListener("touchend", handleTouchEnd, options);
|
|
5693
|
+
node.addEventListener("touchcancel", handleTouchEnd, options);
|
|
5694
|
+
return () => {
|
|
5695
|
+
node.removeEventListener("wheel", handleWheel, options);
|
|
5696
|
+
node.removeEventListener("touchstart", handleTouchStart, options);
|
|
5697
|
+
node.removeEventListener("touchmove", handleTouchMove, options);
|
|
5698
|
+
node.removeEventListener("touchend", handleTouchEnd, options);
|
|
5699
|
+
node.removeEventListener("touchcancel", handleTouchEnd, options);
|
|
5700
|
+
};
|
|
5701
|
+
}, [handleWheel, handleTouchStart, handleTouchMove, handleTouchEnd]);
|
|
5702
|
+
const getRootProps = React8.useCallback(
|
|
5703
|
+
() => ({
|
|
5704
|
+
className,
|
|
5705
|
+
style,
|
|
5706
|
+
onMouseDown: handleMouseDown,
|
|
5707
|
+
onKeyDown: handleKeyDown,
|
|
5708
|
+
onKeyUp: handleKeyUp,
|
|
5709
|
+
tabIndex: 0,
|
|
5710
|
+
role: "application",
|
|
5711
|
+
"aria-label": "Interactive image cropper",
|
|
5712
|
+
"aria-describedby": descriptionId,
|
|
5713
|
+
"aria-valuemin": minZoom,
|
|
5714
|
+
"aria-valuemax": maxZoom,
|
|
5715
|
+
"aria-valuenow": effectiveZoom,
|
|
5716
|
+
"aria-valuetext": `Zoom: ${Math.round(effectiveZoom * 100)}%`
|
|
5717
|
+
}),
|
|
5718
|
+
[
|
|
5719
|
+
className,
|
|
5720
|
+
style,
|
|
5721
|
+
handleMouseDown,
|
|
5722
|
+
handleKeyDown,
|
|
5723
|
+
handleKeyUp,
|
|
5724
|
+
descriptionId,
|
|
5725
|
+
minZoom,
|
|
5726
|
+
maxZoom,
|
|
5727
|
+
effectiveZoom
|
|
5728
|
+
]
|
|
5729
|
+
);
|
|
5730
|
+
const getImageWrapperStyle = React8.useCallback(
|
|
5731
|
+
() => ({
|
|
5732
|
+
width: imageWrapperWidth,
|
|
5733
|
+
height: imageWrapperHeight,
|
|
5734
|
+
transform: `translate3d(${offsetX}px, ${offsetY}px, 0px) scale(${effectiveZoom})`,
|
|
5735
|
+
position: "absolute",
|
|
5736
|
+
left: `calc(50% - ${imageWrapperWidth / 2}px)`,
|
|
5737
|
+
top: `calc(50% - ${imageWrapperHeight / 2}px)`,
|
|
5738
|
+
willChange: "transform"
|
|
5739
|
+
}),
|
|
5740
|
+
[imageWrapperWidth, imageWrapperHeight, offsetX, offsetY, effectiveZoom]
|
|
5741
|
+
);
|
|
5742
|
+
const getImageProps = React8.useCallback(
|
|
5743
|
+
() => ({
|
|
5744
|
+
src: image,
|
|
5745
|
+
alt: "Image being cropped",
|
|
5746
|
+
draggable: false,
|
|
5747
|
+
"aria-hidden": true
|
|
5748
|
+
}),
|
|
5749
|
+
[image]
|
|
5750
|
+
);
|
|
5751
|
+
const getCropAreaStyle = React8.useCallback(
|
|
5752
|
+
() => ({
|
|
5753
|
+
width: cropAreaWidth,
|
|
5754
|
+
height: cropAreaHeight
|
|
5755
|
+
}),
|
|
5756
|
+
[cropAreaWidth, cropAreaHeight]
|
|
5757
|
+
);
|
|
5758
|
+
const getCropAreaProps = React8.useCallback(
|
|
5759
|
+
() => ({
|
|
5760
|
+
style: getCropAreaStyle(),
|
|
5761
|
+
"aria-hidden": true
|
|
5762
|
+
}),
|
|
5763
|
+
[getCropAreaStyle]
|
|
5764
|
+
);
|
|
5765
|
+
const contextValue = {
|
|
5766
|
+
containerRef,
|
|
5767
|
+
image,
|
|
5768
|
+
imgWidth,
|
|
5769
|
+
imgHeight,
|
|
5770
|
+
cropAreaWidth,
|
|
5771
|
+
cropAreaHeight,
|
|
5772
|
+
imageWrapperWidth,
|
|
5773
|
+
imageWrapperHeight,
|
|
5774
|
+
offsetX,
|
|
5775
|
+
offsetY,
|
|
5776
|
+
effectiveZoom,
|
|
5777
|
+
minZoom,
|
|
5778
|
+
maxZoom,
|
|
5779
|
+
getRootProps,
|
|
5780
|
+
getImageProps,
|
|
5781
|
+
getImageWrapperStyle,
|
|
5782
|
+
getCropAreaProps,
|
|
5783
|
+
getCropAreaStyle,
|
|
5784
|
+
descriptionId
|
|
5785
|
+
};
|
|
5786
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CropperContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, ...getRootProps(), ...restProps, children }) });
|
|
5787
|
+
};
|
|
5788
|
+
var CropperImage = ({ className, ...restProps }) => {
|
|
5789
|
+
const { image, getImageProps, getImageWrapperStyle } = useCropperContext();
|
|
5790
|
+
if (!image) return null;
|
|
5791
|
+
const imageProps = getImageProps();
|
|
5792
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: getImageWrapperStyle(), children: /* @__PURE__ */ jsxRuntime.jsx("img", { ...imageProps, className, ...restProps }) });
|
|
5793
|
+
};
|
|
5794
|
+
var CropperCropArea = ({
|
|
5795
|
+
className,
|
|
5796
|
+
style,
|
|
5797
|
+
...restProps
|
|
5798
|
+
}) => {
|
|
5799
|
+
const { cropAreaWidth, cropAreaHeight, getCropAreaProps, getCropAreaStyle } = useCropperContext();
|
|
5800
|
+
if (cropAreaWidth <= 0 || cropAreaHeight <= 0) return null;
|
|
5801
|
+
const areaProps = getCropAreaProps();
|
|
5802
|
+
const areaStyle = getCropAreaStyle();
|
|
5803
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5804
|
+
"div",
|
|
5805
|
+
{
|
|
5806
|
+
...areaProps,
|
|
5807
|
+
style: { ...areaProps.style, ...style, ...areaStyle },
|
|
5808
|
+
className,
|
|
5809
|
+
...restProps
|
|
5810
|
+
}
|
|
5811
|
+
);
|
|
5812
|
+
};
|
|
5813
|
+
var CropperDescription = ({
|
|
5814
|
+
children,
|
|
5815
|
+
className,
|
|
5816
|
+
...restProps
|
|
5817
|
+
}) => {
|
|
5818
|
+
const { descriptionId } = useCropperContext();
|
|
5819
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { id: descriptionId, className, ...restProps, children: children ?? // Default description if none provided by user
|
|
5820
|
+
"Use mouse wheel or pinch gesture to zoom. Drag with mouse or touch, or use arrow keys to pan the image within the crop area." });
|
|
5821
|
+
};
|
|
5822
|
+
var Cropper = {
|
|
5823
|
+
Root: CropperRoot,
|
|
5824
|
+
Image: CropperImage,
|
|
5825
|
+
CropArea: CropperCropArea,
|
|
5826
|
+
Description: CropperDescription
|
|
5827
|
+
};
|
|
5828
|
+
function Slider({
|
|
5829
|
+
className,
|
|
5830
|
+
defaultValue,
|
|
5831
|
+
value,
|
|
5832
|
+
min = 0,
|
|
5833
|
+
max = 100,
|
|
5834
|
+
...props
|
|
5835
|
+
}) {
|
|
5836
|
+
const [internalValues, setInternalValues] = React8__namespace.useState(
|
|
5837
|
+
Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]
|
|
5838
|
+
);
|
|
5839
|
+
React8__namespace.useEffect(() => {
|
|
5840
|
+
if (value !== void 0) {
|
|
5841
|
+
setInternalValues(Array.isArray(value) ? value : [value]);
|
|
5842
|
+
}
|
|
5843
|
+
}, [value]);
|
|
5844
|
+
const handleValueChange = (newValue) => {
|
|
5845
|
+
setInternalValues(newValue);
|
|
5846
|
+
props.onValueChange?.(newValue);
|
|
5847
|
+
};
|
|
5848
|
+
const renderThumb = () => {
|
|
5849
|
+
const thumb = /* @__PURE__ */ jsxRuntime.jsx(
|
|
5850
|
+
SliderPrimitive__namespace.Thumb,
|
|
5851
|
+
{
|
|
5852
|
+
"data-testid": "slider-thumb",
|
|
5853
|
+
"data-slot": "slider-thumb",
|
|
5854
|
+
className: "nebula-ds border-2 border-slider-rangeColor bg-slider-thumbColor ring-ring/50 block size-4 shrink-0 rounded-full transition-[color,box-shadow] outline-none hover:ring-4 focus-visible:ring-4 disabled:pointer-events-none disabled:opacity-50"
|
|
5855
|
+
}
|
|
5856
|
+
);
|
|
5857
|
+
return thumb;
|
|
5858
|
+
};
|
|
5859
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5860
|
+
SliderPrimitive__namespace.Root,
|
|
5861
|
+
{
|
|
5862
|
+
"data-slot": "slider",
|
|
5863
|
+
defaultValue,
|
|
5864
|
+
value,
|
|
5865
|
+
min,
|
|
5866
|
+
max,
|
|
5867
|
+
className: cn(
|
|
5868
|
+
"relative flex w-full touch-none items-center select-none data-[disabled]:opacity-50 data-[orientation=vertical]:h-full data-[orientation=vertical]:min-h-44 data-[orientation=vertical]:w-auto data-[orientation=vertical]:flex-col",
|
|
5869
|
+
className
|
|
5870
|
+
),
|
|
5871
|
+
onValueChange: handleValueChange,
|
|
5872
|
+
...props,
|
|
5873
|
+
children: [
|
|
5874
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5875
|
+
SliderPrimitive__namespace.Track,
|
|
5876
|
+
{
|
|
5877
|
+
"data-slot": "slider-track",
|
|
5878
|
+
className: cn(
|
|
5879
|
+
"bg-slider-trackColor relative grow overflow-hidden rounded-full data-[orientation=horizontal]:h-1.5 data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-1.5"
|
|
5880
|
+
),
|
|
5881
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5882
|
+
SliderPrimitive__namespace.Range,
|
|
5883
|
+
{
|
|
5884
|
+
"data-slot": "slider-range",
|
|
5885
|
+
className: cn(
|
|
5886
|
+
"bg-slider-rangeColor absolute data-[orientation=horizontal]:h-full data-[orientation=vertical]:w-full"
|
|
5887
|
+
)
|
|
5888
|
+
}
|
|
5889
|
+
)
|
|
5890
|
+
}
|
|
5891
|
+
),
|
|
5892
|
+
Array.from({ length: internalValues.length }, (_, index) => /* @__PURE__ */ jsxRuntime.jsx(React8__namespace.Fragment, { children: renderThumb() }, index))
|
|
5893
|
+
]
|
|
5894
|
+
}
|
|
5895
|
+
);
|
|
5896
|
+
}
|
|
5897
|
+
function CropperRoot2({
|
|
5898
|
+
className,
|
|
5899
|
+
...props
|
|
5900
|
+
}) {
|
|
5901
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5902
|
+
Cropper.Root,
|
|
5903
|
+
{
|
|
5904
|
+
"data-slot": "cropper",
|
|
5905
|
+
className: cn(
|
|
5906
|
+
"relative flex w-full cursor-move touch-none items-center justify-center overflow-hidden focus:outline-none",
|
|
5907
|
+
className
|
|
5908
|
+
),
|
|
5909
|
+
...props
|
|
5910
|
+
}
|
|
5911
|
+
);
|
|
5912
|
+
}
|
|
5913
|
+
function CropperDescription2({
|
|
5914
|
+
className,
|
|
5915
|
+
...props
|
|
5916
|
+
}) {
|
|
5917
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5918
|
+
Cropper.Description,
|
|
5919
|
+
{
|
|
5920
|
+
"data-slot": "cropper-description",
|
|
5921
|
+
className: cn("sr-only", className),
|
|
5922
|
+
...props
|
|
5923
|
+
}
|
|
5924
|
+
);
|
|
5925
|
+
}
|
|
5926
|
+
function CropperImage2({
|
|
5927
|
+
className,
|
|
5928
|
+
...props
|
|
5929
|
+
}) {
|
|
5930
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5931
|
+
Cropper.Image,
|
|
5932
|
+
{
|
|
5933
|
+
"data-slot": "cropper-image",
|
|
5934
|
+
className: cn(
|
|
5935
|
+
"pointer-events-none h-full w-full object-cover",
|
|
5936
|
+
className
|
|
5937
|
+
),
|
|
5938
|
+
...props
|
|
5939
|
+
}
|
|
5940
|
+
);
|
|
5941
|
+
}
|
|
5942
|
+
function CropperCropArea2({
|
|
5943
|
+
className,
|
|
5944
|
+
rounded,
|
|
5945
|
+
...props
|
|
5946
|
+
}) {
|
|
5947
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5948
|
+
Cropper.CropArea,
|
|
5949
|
+
{
|
|
5950
|
+
"data-slot": "cropper-crop-area",
|
|
5951
|
+
className: cn(
|
|
5952
|
+
"pointer-events-none absolute shadow-[0_0_0_9999px_rgba(0,0,0,0.5)] in-[[data-slot=cropper]:focus-visible]:ring-[2px] in-[[data-slot=cropper]:focus-visible]:ring-white",
|
|
5953
|
+
rounded && "rounded-full",
|
|
5954
|
+
className,
|
|
5955
|
+
"border-2 border-cropper-cropAreaBorderColor"
|
|
5956
|
+
),
|
|
5957
|
+
...props
|
|
5958
|
+
}
|
|
5959
|
+
);
|
|
5960
|
+
}
|
|
5961
|
+
var createImage = (url) => new Promise((resolve, reject) => {
|
|
5962
|
+
const image = new Image();
|
|
5963
|
+
image.addEventListener("load", () => resolve(image));
|
|
5964
|
+
image.addEventListener("error", (error2) => reject(error2));
|
|
5965
|
+
image.setAttribute("crossOrigin", "anonymous");
|
|
5966
|
+
image.src = url;
|
|
5967
|
+
});
|
|
5968
|
+
async function getCroppedImg(imageSrc, pixelCrop, outputWidth = pixelCrop.width, outputHeight = pixelCrop.height) {
|
|
5969
|
+
try {
|
|
5970
|
+
const image = await createImage(imageSrc);
|
|
5971
|
+
const canvas = document.createElement("canvas");
|
|
5972
|
+
const ctx = canvas.getContext("2d");
|
|
5973
|
+
if (!ctx) {
|
|
5974
|
+
return null;
|
|
5975
|
+
}
|
|
5976
|
+
canvas.width = outputWidth;
|
|
5977
|
+
canvas.height = outputHeight;
|
|
5978
|
+
ctx.drawImage(
|
|
5979
|
+
image,
|
|
5980
|
+
pixelCrop.x,
|
|
5981
|
+
pixelCrop.y,
|
|
5982
|
+
pixelCrop.width,
|
|
5983
|
+
pixelCrop.height,
|
|
5984
|
+
0,
|
|
5985
|
+
0,
|
|
5986
|
+
outputWidth,
|
|
5987
|
+
// Draw onto the output size
|
|
5988
|
+
outputHeight
|
|
5989
|
+
);
|
|
5990
|
+
return new Promise((resolve) => {
|
|
5991
|
+
canvas.toBlob((blob) => {
|
|
5992
|
+
resolve(blob);
|
|
5993
|
+
}, "image/jpeg");
|
|
5994
|
+
});
|
|
5995
|
+
} catch (error2) {
|
|
5996
|
+
return null;
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
function Cropper2({
|
|
6000
|
+
onOpenChange,
|
|
6001
|
+
open,
|
|
6002
|
+
previewUrl,
|
|
6003
|
+
onRemove: removeFile,
|
|
6004
|
+
onApply,
|
|
6005
|
+
rounded = false,
|
|
6006
|
+
portal = false
|
|
6007
|
+
}) {
|
|
6008
|
+
const [finalImageUrl, setFinalImageUrl] = React8.useState(null);
|
|
6009
|
+
const [croppedAreaPixels, setCroppedAreaPixels] = React8.useState(null);
|
|
6010
|
+
const [zoom, setZoom] = React8.useState(1);
|
|
6011
|
+
const handleCropChange = React8.useCallback((pixels) => {
|
|
6012
|
+
setCroppedAreaPixels(pixels);
|
|
6013
|
+
}, []);
|
|
6014
|
+
const handleApply = async () => {
|
|
6015
|
+
if (!previewUrl || !croppedAreaPixels) {
|
|
6016
|
+
if (previewUrl) {
|
|
6017
|
+
removeFile();
|
|
6018
|
+
setCroppedAreaPixels(null);
|
|
6019
|
+
}
|
|
6020
|
+
return;
|
|
6021
|
+
}
|
|
6022
|
+
try {
|
|
6023
|
+
const croppedBlob = await getCroppedImg(previewUrl, croppedAreaPixels);
|
|
6024
|
+
if (!croppedBlob) {
|
|
6025
|
+
throw new Error("Failed to generate cropped image blob.");
|
|
6026
|
+
}
|
|
6027
|
+
const newFinalUrl = URL.createObjectURL(croppedBlob);
|
|
6028
|
+
if (finalImageUrl) {
|
|
6029
|
+
URL.revokeObjectURL(finalImageUrl);
|
|
6030
|
+
}
|
|
6031
|
+
setFinalImageUrl(newFinalUrl);
|
|
6032
|
+
onOpenChange(false);
|
|
6033
|
+
onApply(newFinalUrl, croppedBlob);
|
|
6034
|
+
} catch (error2) {
|
|
6035
|
+
onOpenChange(false);
|
|
6036
|
+
}
|
|
6037
|
+
};
|
|
6038
|
+
React8.useEffect(() => {
|
|
6039
|
+
const currentFinalUrl = finalImageUrl;
|
|
6040
|
+
return () => {
|
|
6041
|
+
if (currentFinalUrl && currentFinalUrl.startsWith("blob:")) {
|
|
6042
|
+
URL.revokeObjectURL(currentFinalUrl);
|
|
6043
|
+
}
|
|
6044
|
+
};
|
|
6045
|
+
}, [finalImageUrl]);
|
|
6046
|
+
const { cropper } = useNebulaI18n().messages;
|
|
6047
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6048
|
+
DialogContent,
|
|
6049
|
+
{
|
|
6050
|
+
className: "nebula-ds gap-0 p-0 sm:max-w-140 *:[button]:hidden border border-cropper-dialogBorderColor",
|
|
6051
|
+
portal,
|
|
6052
|
+
showClose: false,
|
|
6053
|
+
onOpenAutoFocus: (e) => e.preventDefault(),
|
|
6054
|
+
children: [
|
|
6055
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogHeader, { className: "nebula-ds contents space-y-0 text-left", children: /* @__PURE__ */ jsxRuntime.jsxs(DialogTitle, { className: "nebula-ds flex items-center justify-between p-4 text-base", children: [
|
|
6056
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nebula-ds flex items-center gap-2", children: [
|
|
6057
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6058
|
+
Button,
|
|
6059
|
+
{
|
|
6060
|
+
icon: true,
|
|
6061
|
+
type: "button",
|
|
6062
|
+
variant: "ghost",
|
|
6063
|
+
onClick: () => onOpenChange(false),
|
|
6064
|
+
"aria-label": "close-cropper-dialog",
|
|
6065
|
+
"data-testid": "close-cropper-dialog",
|
|
6066
|
+
size: "sm",
|
|
6067
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { "aria-hidden": "true" })
|
|
6068
|
+
}
|
|
6069
|
+
),
|
|
6070
|
+
cropper.dialogTitle
|
|
6071
|
+
] }),
|
|
6072
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6073
|
+
Button,
|
|
6074
|
+
{
|
|
6075
|
+
autoFocus: false,
|
|
6076
|
+
onClick: handleApply,
|
|
6077
|
+
disabled: !previewUrl,
|
|
6078
|
+
variant: "primary",
|
|
6079
|
+
size: "sm",
|
|
6080
|
+
type: "button",
|
|
6081
|
+
"aria-label": "apply-cropper-image",
|
|
6082
|
+
"data-testid": "apply-cropper-image",
|
|
6083
|
+
children: cropper.applyButtonLabel
|
|
6084
|
+
}
|
|
6085
|
+
)
|
|
6086
|
+
] }) }),
|
|
6087
|
+
previewUrl && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6088
|
+
CropperRoot2,
|
|
6089
|
+
{
|
|
6090
|
+
className: "nebula-ds h-96 sm:h-120",
|
|
6091
|
+
image: previewUrl,
|
|
6092
|
+
zoom,
|
|
6093
|
+
onCropChange: handleCropChange,
|
|
6094
|
+
onZoomChange: setZoom,
|
|
6095
|
+
children: [
|
|
6096
|
+
/* @__PURE__ */ jsxRuntime.jsx(CropperDescription2, {}),
|
|
6097
|
+
/* @__PURE__ */ jsxRuntime.jsx(CropperImage2, {}),
|
|
6098
|
+
/* @__PURE__ */ jsxRuntime.jsx(CropperCropArea2, { rounded })
|
|
6099
|
+
]
|
|
6100
|
+
}
|
|
6101
|
+
),
|
|
6102
|
+
/* @__PURE__ */ jsxRuntime.jsx(DialogFooter, { className: "nebula-ds border-t border-t-cropper-dialogBorderColor px-4 py-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "nebula-ds mx-auto flex w-full max-w-80 items-center gap-4", children: [
|
|
6103
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6104
|
+
lucideReact.MinusIcon,
|
|
6105
|
+
{
|
|
6106
|
+
className: "nebula-ds shrink-0 size-5 text-cropper-sliderIconColor",
|
|
6107
|
+
size: 16,
|
|
6108
|
+
"aria-hidden": "true"
|
|
6109
|
+
}
|
|
6110
|
+
),
|
|
6111
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6112
|
+
Slider,
|
|
6113
|
+
{
|
|
6114
|
+
defaultValue: [1],
|
|
6115
|
+
value: [zoom],
|
|
6116
|
+
min: 1,
|
|
6117
|
+
max: 3,
|
|
6118
|
+
step: 0.1,
|
|
6119
|
+
onValueChange: (value) => setZoom(value[0]),
|
|
6120
|
+
"aria-label": "Zoom slider"
|
|
6121
|
+
}
|
|
6122
|
+
),
|
|
6123
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6124
|
+
lucideReact.PlusIcon,
|
|
6125
|
+
{
|
|
6126
|
+
className: "nebula-ds shrink-0 size-5 text-cropper-sliderIconColor",
|
|
6127
|
+
size: 16,
|
|
6128
|
+
"aria-hidden": "true"
|
|
6129
|
+
}
|
|
6130
|
+
)
|
|
6131
|
+
] }) })
|
|
6132
|
+
]
|
|
6133
|
+
}
|
|
6134
|
+
) });
|
|
6135
|
+
}
|
|
5077
6136
|
|
|
5078
6137
|
// src/tailwind.ts
|
|
5079
6138
|
function content({ base = "./" } = {}) {
|
|
@@ -5127,6 +6186,10 @@ exports.Calendar = Calendar;
|
|
|
5127
6186
|
exports.Caption = Caption;
|
|
5128
6187
|
exports.Checkbox = Checkbox;
|
|
5129
6188
|
exports.Creatable = StyledCreatable;
|
|
6189
|
+
exports.Cropper = Cropper2;
|
|
6190
|
+
exports.CropperCropArea = CropperCropArea2;
|
|
6191
|
+
exports.CropperDescription = CropperDescription2;
|
|
6192
|
+
exports.CropperImage = CropperImage2;
|
|
5130
6193
|
exports.Dialog = Dialog;
|
|
5131
6194
|
exports.DialogBody = DialogBody;
|
|
5132
6195
|
exports.DialogClose = DialogClose;
|
|
@@ -5181,6 +6244,7 @@ exports.ProfileImage = ProfileImage;
|
|
|
5181
6244
|
exports.Select = StyledSelect;
|
|
5182
6245
|
exports.Separator = Separator2;
|
|
5183
6246
|
exports.Skeleton = Skeleton;
|
|
6247
|
+
exports.Slider = Slider;
|
|
5184
6248
|
exports.Space = Space;
|
|
5185
6249
|
exports.SpaceDirectionEnum = SpaceDirectionEnum;
|
|
5186
6250
|
exports.SpaceSizeEnum = SpaceSizeEnum;
|
|
@@ -5211,7 +6275,7 @@ exports.dateIsAvailable = dateIsAvailable;
|
|
|
5211
6275
|
exports.formatBytes = formatBytes;
|
|
5212
6276
|
exports.getNebulaLanguage = getNebulaLanguage;
|
|
5213
6277
|
exports.localeByi18nKey = localeByi18nKey;
|
|
5214
|
-
exports.messages =
|
|
6278
|
+
exports.messages = messages19;
|
|
5215
6279
|
exports.separatorVariants = separatorVariants;
|
|
5216
6280
|
exports.setNebulaLanguage = setNebulaLanguage;
|
|
5217
6281
|
exports.tagVariantsEnum = tagVariantsEnum;
|