@choice-ui/react 1.6.8 → 1.6.9
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/components/description/dist/index.d.ts +8 -0
- package/dist/components/description/dist/index.js +29 -0
- package/dist/components/description/src/description.d.ts +6 -0
- package/dist/components/description/src/description.js +18 -0
- package/dist/components/description/src/index.d.ts +2 -0
- package/dist/components/description/src/tv.d.ts +13 -0
- package/dist/components/description/src/tv.js +15 -0
- package/dist/components/description/tsup.config.d.ts +2 -0
- package/dist/components/emoji-picker/dist/index.d.ts +1 -0
- package/dist/components/emoji-picker/dist/index.js +4 -2
- package/dist/components/emoji-picker/src/emoji-picker.d.ts +1 -0
- package/dist/components/emoji-picker/src/emoji-picker.js +4 -2
- package/dist/components/error-message/dist/index.d.ts +8 -0
- package/dist/components/error-message/dist/index.js +30 -0
- package/dist/components/error-message/src/error-message.d.ts +6 -0
- package/dist/components/error-message/src/error-message.js +19 -0
- package/dist/components/error-message/src/index.d.ts +2 -0
- package/dist/components/error-message/src/tv.d.ts +13 -0
- package/dist/components/error-message/src/tv.js +15 -0
- package/dist/components/error-message/tsup.config.d.ts +2 -0
- package/dist/components/form/src/adapters/base-adapter.js +4 -2
- package/dist/components/form/src/tv.d.ts +0 -12
- package/dist/components/form/src/tv.js +1 -13
- package/dist/components/index.d.ts +3 -0
- package/dist/components/md-render/dist/index.d.ts +2 -1
- package/dist/components/md-render/dist/index.js +3 -1
- package/dist/components/md-render/src/md-render.js +3 -1
- package/dist/components/md-render/src/types.d.ts +2 -1
- package/dist/components/notifications/dist/index.d.ts +1 -5
- package/dist/components/notifications/src/notifications.d.ts +0 -1
- package/dist/components/notifications/src/notifications.js +0 -1
- package/dist/components/numeric-input/dist/index.d.ts +23 -9
- package/dist/components/numeric-input/dist/index.js +26 -3
- package/dist/components/numeric-input/src/components/numeric-input-menu-trigger.js +4 -1
- package/dist/components/numeric-input/src/hooks/index.d.ts +1 -0
- package/dist/components/numeric-input/src/hooks/use-numeric-long-press.d.ts +13 -0
- package/dist/components/numeric-input/src/hooks/use-numeric-long-press.js +27 -0
- package/dist/components/numeric-input/src/index.d.ts +1 -0
- package/dist/components/numeric-input/src/tv.js +22 -2
- package/dist/components/picture-preview/dist/index.d.ts +5 -0
- package/dist/components/picture-preview/dist/index.js +287 -140
- package/dist/components/picture-preview/src/hooks/useWheelHandler.d.ts +6 -1
- package/dist/components/picture-preview/src/hooks/useWheelHandler.js +25 -7
- package/dist/components/picture-preview/src/picture-preview.d.ts +5 -0
- package/dist/components/picture-preview/src/picture-preview.js +214 -123
- package/dist/components/picture-preview/src/tv.d.ts +93 -3
- package/dist/components/picture-preview/src/tv.js +48 -10
- package/dist/components/separator/dist/index.d.ts +1 -8
- package/dist/components/separator/src/separator.d.ts +1 -8
- package/dist/components/separator/src/separator.js +33 -5
- package/dist/components/separator/src/tv.d.ts +39 -18
- package/dist/components/separator/src/tv.js +37 -7
- package/dist/components/text-field/dist/index.d.ts +2 -3
- package/dist/components/text-field/dist/index.js +4 -19
- package/dist/components/text-field/src/components/index.d.ts +0 -1
- package/dist/components/text-field/src/text-field.d.ts +3 -2
- package/dist/components/text-field/src/text-field.js +2 -2
- package/dist/components/text-field/src/tv.d.ts +3 -3
- package/dist/components/text-field/src/tv.js +1 -6
- package/dist/components/toast/dist/index.d.ts +248 -0
- package/dist/components/toast/src/components/index.d.ts +3 -0
- package/dist/components/toast/src/components/toast-progress-bar.d.ts +7 -0
- package/dist/components/toast/src/components/toast-progress-bar.js +53 -0
- package/dist/components/toast/src/components/toaster-item.d.ts +26 -0
- package/dist/components/toast/src/components/toaster-item.js +416 -0
- package/dist/components/toast/src/components/toaster-slots.d.ts +87 -0
- package/dist/components/toast/src/components/toaster-slots.js +38 -0
- package/dist/components/toast/src/index.d.ts +5 -0
- package/dist/components/toast/src/store.d.ts +101 -0
- package/dist/components/toast/src/store.js +205 -0
- package/dist/components/toast/src/toaster.d.ts +87 -0
- package/dist/components/toast/src/toaster.js +271 -0
- package/dist/components/toast/src/tv.d.ts +365 -0
- package/dist/components/toast/src/tv.js +412 -0
- package/dist/components/toast/src/types.d.ts +79 -0
- package/dist/components/toast/tsup.config.d.ts +2 -0
- package/dist/index.js +11 -2
- package/dist/styles/components.css +2 -0
- package/package.json +1 -1
- package/dist/components/text-field/src/components/field-description.d.ts +0 -2
- package/dist/components/text-field/src/components/field-description.js +0 -16
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Dropdown as Dropdown2 } from "../../dropdown/dist/index.js";
|
|
2
2
|
import { IconButton } from "../../icon-button/dist/index.js";
|
|
3
3
|
import { LoaderCircle, ImageRemove, Delete, Add } from "@choiceform/icons-react";
|
|
4
|
-
import { forwardRef, useState,
|
|
4
|
+
import { forwardRef, useState, useMemo, useRef, useEffect, useCallback } from "react";
|
|
5
|
+
import { useEventCallback } from "usehooks-ts";
|
|
5
6
|
import isHotkey from "is-hotkey";
|
|
6
7
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
7
8
|
import { tcv, tcx } from "../../../shared/utils/tcx/tcx.js";
|
|
@@ -91,7 +92,7 @@ function useDraggable(initialPosition = { x: 0, y: 0 }, options = {}) {
|
|
|
91
92
|
};
|
|
92
93
|
}
|
|
93
94
|
function useWheelHandler(targetRef, zoomRef, positionRef, options = {}) {
|
|
94
|
-
const { minZoom = 0.01, maxZoom = 10, zoomStep = 0.1, onZoom, onPan } = options;
|
|
95
|
+
const { minZoom = 0.01, maxZoom = 10, zoomStep = 0.1, onZoom, onPan, onZoomAtPoint } = options;
|
|
95
96
|
const isMac = useRef(
|
|
96
97
|
typeof navigator !== "undefined" && navigator.platform.toUpperCase().indexOf("MAC") >= 0
|
|
97
98
|
);
|
|
@@ -100,14 +101,32 @@ function useWheelHandler(targetRef, zoomRef, positionRef, options = {}) {
|
|
|
100
101
|
(event) => {
|
|
101
102
|
event.preventDefault();
|
|
102
103
|
event.stopPropagation();
|
|
103
|
-
if (
|
|
104
|
+
if (zoomRef.current === void 0 || zoomRef.current === null || !positionRef.current) return;
|
|
104
105
|
const isPreciseEvent = event.deltaMode === 0;
|
|
105
106
|
const hasDeltaX = Math.abs(event.deltaX) > 0;
|
|
106
107
|
const isZoomModifier = isMac.current && event.metaKey || !isMac.current && event.ctrlKey;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
108
|
+
const isPinchZoom = event.ctrlKey && isPreciseEvent && !event.metaKey;
|
|
109
|
+
if (isZoomModifier || isPinchZoom) {
|
|
110
|
+
const oldZoom = zoomRef.current;
|
|
111
|
+
let newZoom;
|
|
112
|
+
const sensitivity = isPinchZoom ? 8e-3 : 3e-3;
|
|
113
|
+
const delta = event.deltaY;
|
|
114
|
+
newZoom = oldZoom * Math.exp(-delta * sensitivity);
|
|
115
|
+
newZoom = Math.max(minZoom, Math.min(maxZoom, newZoom));
|
|
116
|
+
const target = targetRef.current;
|
|
117
|
+
if (onZoomAtPoint && target) {
|
|
118
|
+
const rect = target.getBoundingClientRect();
|
|
119
|
+
const mouseX = event.clientX - rect.left - rect.width / 2;
|
|
120
|
+
const mouseY = event.clientY - rect.top - rect.height / 2;
|
|
121
|
+
const zoomRatio = newZoom / oldZoom;
|
|
122
|
+
const currentX = positionRef.current.x;
|
|
123
|
+
const currentY = positionRef.current.y;
|
|
124
|
+
const newPosition = {
|
|
125
|
+
x: mouseX - (mouseX - currentX) * zoomRatio,
|
|
126
|
+
y: mouseY - (mouseY - currentY) * zoomRatio
|
|
127
|
+
};
|
|
128
|
+
onZoomAtPoint({ newZoom, newPosition });
|
|
129
|
+
} else if (onZoom) {
|
|
111
130
|
onZoom(newZoom);
|
|
112
131
|
}
|
|
113
132
|
} else if (isPreciseEvent && hasDeltaX) {
|
|
@@ -139,7 +158,7 @@ function useWheelHandler(targetRef, zoomRef, positionRef, options = {}) {
|
|
|
139
158
|
}
|
|
140
159
|
}
|
|
141
160
|
},
|
|
142
|
-
[minZoom, maxZoom,
|
|
161
|
+
[minZoom, maxZoom, onZoom, onPan, onZoomAtPoint, targetRef]
|
|
143
162
|
);
|
|
144
163
|
const handleKeyDown = useCallback((e) => {
|
|
145
164
|
if (isMac.current && e.metaKey || !isMac.current && e.ctrlKey) {
|
|
@@ -200,24 +219,30 @@ var HOTKEYS = {
|
|
|
200
219
|
};
|
|
201
220
|
var PicturePreviewTv = tcv({
|
|
202
221
|
slots: {
|
|
203
|
-
root: [
|
|
222
|
+
root: [
|
|
223
|
+
"group/picture-preview relative flex flex-col overflow-hidden",
|
|
224
|
+
"h-full w-full",
|
|
225
|
+
"touch-none select-none"
|
|
226
|
+
],
|
|
204
227
|
loading: [
|
|
205
228
|
"text-secondary-foreground absolute inset-0 z-10 flex flex-col items-center justify-center gap-4"
|
|
206
229
|
],
|
|
207
230
|
content: [
|
|
208
231
|
"relative flex-1 overflow-hidden",
|
|
209
232
|
"flex items-center justify-center",
|
|
210
|
-
"bg-gray-100 dark:bg-gray-900"
|
|
233
|
+
"bg-gray-100 dark:bg-gray-900",
|
|
234
|
+
"[contain:layout_paint]"
|
|
211
235
|
],
|
|
212
236
|
canvas: [
|
|
213
|
-
"relative h-full w-full",
|
|
214
237
|
"transform-gpu will-change-transform",
|
|
215
|
-
"cursor-grab active:cursor-grabbing"
|
|
238
|
+
"cursor-grab active:cursor-grabbing",
|
|
239
|
+
"origin-center",
|
|
240
|
+
"flex items-center justify-center"
|
|
216
241
|
],
|
|
217
|
-
image: ["pointer-events-none", "
|
|
242
|
+
image: ["pointer-events-none", "block w-auto h-auto"],
|
|
218
243
|
controlGroup: [
|
|
219
244
|
"overflow-hidden",
|
|
220
|
-
"absolute
|
|
245
|
+
"absolute flex items-center",
|
|
221
246
|
"bg-default-background",
|
|
222
247
|
"rounded-md",
|
|
223
248
|
"shadow-md"
|
|
@@ -226,26 +251,58 @@ var PicturePreviewTv = tcv({
|
|
|
226
251
|
variants: {
|
|
227
252
|
isLoading: {
|
|
228
253
|
true: {
|
|
229
|
-
image: "opacity-0 transition-
|
|
254
|
+
image: "opacity-0 scale-105 blur-sm transition-all duration-500 ease-out"
|
|
230
255
|
},
|
|
231
|
-
false: {
|
|
256
|
+
false: {
|
|
257
|
+
image: "opacity-100 scale-100 blur-0 transition-all duration-500 ease-out"
|
|
258
|
+
}
|
|
232
259
|
},
|
|
233
260
|
isError: {
|
|
234
261
|
true: {
|
|
235
|
-
image: "opacity-0
|
|
262
|
+
image: "opacity-0"
|
|
263
|
+
},
|
|
264
|
+
false: {}
|
|
265
|
+
},
|
|
266
|
+
isMenuOpen: {
|
|
267
|
+
true: {
|
|
268
|
+
controlGroup: "opacity-100"
|
|
236
269
|
},
|
|
237
270
|
false: {}
|
|
271
|
+
},
|
|
272
|
+
controlPosition: {
|
|
273
|
+
"top-left": {
|
|
274
|
+
controlGroup: "top-2 left-2"
|
|
275
|
+
},
|
|
276
|
+
"top-right": {
|
|
277
|
+
controlGroup: "top-2 right-2"
|
|
278
|
+
},
|
|
279
|
+
"bottom-left": {
|
|
280
|
+
controlGroup: "bottom-2 left-2"
|
|
281
|
+
},
|
|
282
|
+
"bottom-right": {
|
|
283
|
+
controlGroup: "bottom-2 right-2"
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
controlShow: {
|
|
287
|
+
always: {
|
|
288
|
+
controlGroup: ""
|
|
289
|
+
},
|
|
290
|
+
hover: {
|
|
291
|
+
controlGroup: "group-hover/picture-preview:opacity-100 opacity-0 transition-opacity duration-200"
|
|
292
|
+
}
|
|
238
293
|
}
|
|
239
294
|
},
|
|
240
295
|
defaultVariants: {
|
|
241
296
|
isLoading: false,
|
|
242
|
-
isError: false
|
|
297
|
+
isError: false,
|
|
298
|
+
isMenuOpen: false,
|
|
299
|
+
controlPosition: "bottom-right"
|
|
243
300
|
}
|
|
244
301
|
});
|
|
245
|
-
var MIN_ZOOM = 0.01;
|
|
246
|
-
var MAX_ZOOM = 10;
|
|
247
302
|
var ZOOM_STEP = 0.1;
|
|
248
303
|
var INITIAL_ZOOM = 1;
|
|
304
|
+
var MIN_ACTUAL_PERCENT = 2;
|
|
305
|
+
var MAX_ACTUAL_PERCENT = 1e3;
|
|
249
306
|
var PicturePreview = forwardRef(
|
|
250
307
|
function PicturePreview2(props, ref) {
|
|
251
308
|
const {
|
|
@@ -263,16 +320,32 @@ var PicturePreview = forwardRef(
|
|
|
263
320
|
zoomTo200: "Zoom to 200%",
|
|
264
321
|
error: "Image loading failed, please try again."
|
|
265
322
|
},
|
|
323
|
+
control = {
|
|
324
|
+
enable: true,
|
|
325
|
+
position: "bottom-right",
|
|
326
|
+
show: "hover"
|
|
327
|
+
},
|
|
266
328
|
...rest
|
|
267
329
|
} = props;
|
|
268
330
|
const [zoom, setZoom] = useState(INITIAL_ZOOM);
|
|
269
331
|
const [isLoading, setIsLoading] = useState(true);
|
|
270
332
|
const [isError, setIsError] = useState(false);
|
|
333
|
+
const [naturalSize, setNaturalSize] = useState(null);
|
|
334
|
+
const [baseScale, setBaseScale] = useState(1);
|
|
335
|
+
const [menuIsOpen, setMenuIsOpen] = useState(false);
|
|
336
|
+
const minZoom = useMemo(
|
|
337
|
+
() => baseScale > 0 ? MIN_ACTUAL_PERCENT / 100 / baseScale : 0.01,
|
|
338
|
+
[baseScale]
|
|
339
|
+
);
|
|
340
|
+
const maxZoom = useMemo(
|
|
341
|
+
() => baseScale > 0 ? MAX_ACTUAL_PERCENT / 100 / baseScale : 100,
|
|
342
|
+
[baseScale]
|
|
343
|
+
);
|
|
271
344
|
const internalRef = useRef(null);
|
|
272
345
|
const canvasRef = useRef(null);
|
|
273
346
|
const zoomRef = useRef(zoom);
|
|
274
347
|
const rafId = useRef(null);
|
|
275
|
-
const scheduleUpdate =
|
|
348
|
+
const scheduleUpdate = useEventCallback(() => {
|
|
276
349
|
if (rafId.current !== null) {
|
|
277
350
|
return;
|
|
278
351
|
}
|
|
@@ -280,7 +353,7 @@ var PicturePreview = forwardRef(
|
|
|
280
353
|
setZoom(zoomRef.current);
|
|
281
354
|
rafId.current = null;
|
|
282
355
|
});
|
|
283
|
-
}
|
|
356
|
+
});
|
|
284
357
|
useEffect(() => {
|
|
285
358
|
return () => {
|
|
286
359
|
if (rafId.current !== null) {
|
|
@@ -292,43 +365,52 @@ var PicturePreview = forwardRef(
|
|
|
292
365
|
zoomRef.current = zoom;
|
|
293
366
|
}, [zoom]);
|
|
294
367
|
const { position, isDragging, handleMouseDown, updatePosition, positionRef } = useDraggable();
|
|
295
|
-
const
|
|
296
|
-
(
|
|
297
|
-
|
|
368
|
+
const handlePositionChange = useEventCallback((newPosition) => {
|
|
369
|
+
updatePosition(newPosition);
|
|
370
|
+
});
|
|
371
|
+
const handleZoomAtPoint = useEventCallback(
|
|
372
|
+
(params) => {
|
|
373
|
+
zoomRef.current = params.newZoom;
|
|
374
|
+
updatePosition(params.newPosition);
|
|
298
375
|
scheduleUpdate();
|
|
299
|
-
}
|
|
300
|
-
[scheduleUpdate]
|
|
301
|
-
);
|
|
302
|
-
const handlePositionChange = useCallback(
|
|
303
|
-
(newPosition) => {
|
|
304
|
-
updatePosition(newPosition);
|
|
305
|
-
},
|
|
306
|
-
[updatePosition]
|
|
376
|
+
}
|
|
307
377
|
);
|
|
308
378
|
useWheelHandler(internalRef, zoomRef, positionRef, {
|
|
309
|
-
minZoom
|
|
310
|
-
maxZoom
|
|
379
|
+
minZoom,
|
|
380
|
+
maxZoom,
|
|
311
381
|
zoomStep: ZOOM_STEP,
|
|
312
|
-
|
|
313
|
-
|
|
382
|
+
onPan: handlePositionChange,
|
|
383
|
+
onZoomAtPoint: handleZoomAtPoint
|
|
384
|
+
});
|
|
385
|
+
const zoomIn = useEventCallback(() => {
|
|
386
|
+
zoomRef.current = Math.min(maxZoom, zoomRef.current + ZOOM_STEP);
|
|
387
|
+
scheduleUpdate();
|
|
314
388
|
});
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
}, [handleZoomChange]);
|
|
321
|
-
const resetView = useCallback(() => {
|
|
389
|
+
const zoomOut = useEventCallback(() => {
|
|
390
|
+
zoomRef.current = Math.max(minZoom, zoomRef.current - ZOOM_STEP);
|
|
391
|
+
scheduleUpdate();
|
|
392
|
+
});
|
|
393
|
+
const resetView = useEventCallback(() => {
|
|
322
394
|
zoomRef.current = INITIAL_ZOOM;
|
|
323
395
|
updatePosition({ x: 0, y: 0 });
|
|
324
396
|
scheduleUpdate();
|
|
325
|
-
}
|
|
326
|
-
const fitToView =
|
|
327
|
-
if (!canvasRef.current) return;
|
|
328
|
-
|
|
397
|
+
});
|
|
398
|
+
const fitToView = useEventCallback(() => {
|
|
399
|
+
if (!canvasRef.current || !naturalSize || !internalRef.current || baseScale <= 0) return;
|
|
400
|
+
const containerRect = internalRef.current.getBoundingClientRect();
|
|
401
|
+
const containerWidth = containerRect.width;
|
|
402
|
+
const containerHeight = containerRect.height;
|
|
403
|
+
if (naturalSize.width <= 0 || naturalSize.height <= 0) return;
|
|
404
|
+
const scaleX = containerWidth / naturalSize.width;
|
|
405
|
+
const scaleY = containerHeight / naturalSize.height;
|
|
406
|
+
const fitScale = Math.min(scaleX, scaleY);
|
|
407
|
+
zoomRef.current = fitScale / baseScale;
|
|
329
408
|
updatePosition({ x: 0, y: 0 });
|
|
330
409
|
scheduleUpdate();
|
|
331
|
-
}
|
|
410
|
+
});
|
|
411
|
+
const handleDoubleClick = useEventCallback(() => {
|
|
412
|
+
fitToView();
|
|
413
|
+
});
|
|
332
414
|
useHotkeys([
|
|
333
415
|
{
|
|
334
416
|
hotkey: HOTKEYS.ZOOM_IN,
|
|
@@ -347,13 +429,16 @@ var PicturePreview = forwardRef(
|
|
|
347
429
|
handler: () => fitToView()
|
|
348
430
|
}
|
|
349
431
|
]);
|
|
350
|
-
const handleZoomMenuItemClick =
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
432
|
+
const handleZoomMenuItemClick = useEventCallback((zoomLevel) => {
|
|
433
|
+
zoomRef.current = zoomLevel;
|
|
434
|
+
scheduleUpdate();
|
|
435
|
+
});
|
|
436
|
+
const setActualZoomPercent = useEventCallback((percent) => {
|
|
437
|
+
if (baseScale === 0) return;
|
|
438
|
+
const newZoom = percent / 100 / baseScale;
|
|
439
|
+
zoomRef.current = Math.max(minZoom, Math.min(maxZoom, newZoom));
|
|
440
|
+
scheduleUpdate();
|
|
441
|
+
});
|
|
357
442
|
useEffect(() => {
|
|
358
443
|
if (!internalRef.current) return;
|
|
359
444
|
if (typeof ref === "function") {
|
|
@@ -373,26 +458,75 @@ var PicturePreview = forwardRef(
|
|
|
373
458
|
backfaceVisibility: "hidden"
|
|
374
459
|
};
|
|
375
460
|
}, [position.x, position.y, zoom, isDragging]);
|
|
461
|
+
const calculateBaseScale = useCallback(() => {
|
|
462
|
+
if (!naturalSize || !internalRef.current) return 1;
|
|
463
|
+
const containerRect = internalRef.current.getBoundingClientRect();
|
|
464
|
+
const containerWidth = containerRect.width;
|
|
465
|
+
const containerHeight = containerRect.height;
|
|
466
|
+
if (naturalSize.width <= 0 || naturalSize.height <= 0 || containerWidth <= 0 || containerHeight <= 0) {
|
|
467
|
+
return 1;
|
|
468
|
+
}
|
|
469
|
+
const scaleX = containerWidth / naturalSize.width;
|
|
470
|
+
const scaleY = containerHeight / naturalSize.height;
|
|
471
|
+
return Math.min(scaleX, scaleY);
|
|
472
|
+
}, [naturalSize]);
|
|
473
|
+
useEffect(() => {
|
|
474
|
+
if (!naturalSize || !internalRef.current) return;
|
|
475
|
+
const updateBaseScale = () => {
|
|
476
|
+
const scale = calculateBaseScale();
|
|
477
|
+
setBaseScale(scale);
|
|
478
|
+
};
|
|
479
|
+
updateBaseScale();
|
|
480
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
481
|
+
updateBaseScale();
|
|
482
|
+
});
|
|
483
|
+
resizeObserver.observe(internalRef.current);
|
|
484
|
+
return () => {
|
|
485
|
+
resizeObserver.disconnect();
|
|
486
|
+
};
|
|
487
|
+
}, [naturalSize, calculateBaseScale]);
|
|
376
488
|
useEffect(() => {
|
|
377
489
|
setIsLoading(true);
|
|
490
|
+
setIsError(false);
|
|
491
|
+
setNaturalSize(null);
|
|
378
492
|
const img = new Image();
|
|
379
|
-
|
|
493
|
+
let isCancelled = false;
|
|
380
494
|
img.onload = () => {
|
|
495
|
+
if (isCancelled) return;
|
|
496
|
+
setNaturalSize({ width: img.naturalWidth, height: img.naturalHeight });
|
|
381
497
|
setIsLoading(false);
|
|
382
498
|
};
|
|
383
499
|
img.onerror = () => {
|
|
500
|
+
if (isCancelled) return;
|
|
501
|
+
setIsError(true);
|
|
384
502
|
setIsLoading(false);
|
|
385
503
|
};
|
|
504
|
+
img.src = src;
|
|
505
|
+
return () => {
|
|
506
|
+
isCancelled = true;
|
|
507
|
+
img.onload = null;
|
|
508
|
+
img.onerror = null;
|
|
509
|
+
img.src = "";
|
|
510
|
+
};
|
|
386
511
|
}, [src]);
|
|
387
|
-
const
|
|
512
|
+
const actualZoomPercent = useMemo(() => {
|
|
513
|
+
return Math.round(zoom * baseScale * 100);
|
|
514
|
+
}, [zoom, baseScale]);
|
|
515
|
+
const tv = PicturePreviewTv({
|
|
516
|
+
isLoading,
|
|
517
|
+
isError,
|
|
518
|
+
isMenuOpen: menuIsOpen,
|
|
519
|
+
controlPosition: control.position,
|
|
520
|
+
controlShow: control.show
|
|
521
|
+
});
|
|
388
522
|
return /* @__PURE__ */ jsxs(
|
|
389
523
|
"div",
|
|
390
524
|
{
|
|
391
525
|
ref: internalRef,
|
|
392
|
-
className: tcx(
|
|
526
|
+
className: tcx(tv.root(), className),
|
|
393
527
|
...rest,
|
|
394
528
|
children: [
|
|
395
|
-
isLoading && /* @__PURE__ */ jsx("div", { className:
|
|
529
|
+
isLoading && /* @__PURE__ */ jsx("div", { className: tv.loading(), children: /* @__PURE__ */ jsx(
|
|
396
530
|
LoaderCircle,
|
|
397
531
|
{
|
|
398
532
|
className: "animate-spin",
|
|
@@ -400,7 +534,7 @@ var PicturePreview = forwardRef(
|
|
|
400
534
|
height: 32
|
|
401
535
|
}
|
|
402
536
|
) }),
|
|
403
|
-
isError && /* @__PURE__ */ jsxs("div", { className:
|
|
537
|
+
isError && /* @__PURE__ */ jsxs("div", { className: tv.loading(), children: [
|
|
404
538
|
/* @__PURE__ */ jsx(
|
|
405
539
|
ImageRemove,
|
|
406
540
|
{
|
|
@@ -410,19 +544,24 @@ var PicturePreview = forwardRef(
|
|
|
410
544
|
),
|
|
411
545
|
/* @__PURE__ */ jsx("span", { children: defaultText.error })
|
|
412
546
|
] }),
|
|
413
|
-
/* @__PURE__ */ jsx("div", { className:
|
|
547
|
+
!isError && /* @__PURE__ */ jsx("div", { className: tv.content(), children: /* @__PURE__ */ jsx(
|
|
414
548
|
"div",
|
|
415
549
|
{
|
|
416
550
|
ref: canvasRef,
|
|
417
|
-
className:
|
|
551
|
+
className: tv.canvas(),
|
|
418
552
|
style: transformStyle,
|
|
419
553
|
onMouseDown: handleMouseDown,
|
|
554
|
+
onDoubleClick: handleDoubleClick,
|
|
420
555
|
children: /* @__PURE__ */ jsx(
|
|
421
556
|
"img",
|
|
422
557
|
{
|
|
423
558
|
src,
|
|
424
559
|
alt: fileName || "Preview",
|
|
425
|
-
className:
|
|
560
|
+
className: tv.image(),
|
|
561
|
+
style: naturalSize ? {
|
|
562
|
+
width: naturalSize.width * baseScale,
|
|
563
|
+
height: naturalSize.height * baseScale
|
|
564
|
+
} : void 0,
|
|
426
565
|
draggable: false,
|
|
427
566
|
loading: "eager",
|
|
428
567
|
decoding: "async",
|
|
@@ -432,7 +571,7 @@ var PicturePreview = forwardRef(
|
|
|
432
571
|
)
|
|
433
572
|
}
|
|
434
573
|
) }),
|
|
435
|
-
isError || isLoading ? null : /* @__PURE__ */ jsxs("div", { className:
|
|
574
|
+
isError || isLoading || control.enable === false ? null : /* @__PURE__ */ jsxs("div", { className: tv.controlGroup(), children: [
|
|
436
575
|
/* @__PURE__ */ jsx(
|
|
437
576
|
IconButton,
|
|
438
577
|
{
|
|
@@ -449,82 +588,90 @@ var PicturePreview = forwardRef(
|
|
|
449
588
|
children: /* @__PURE__ */ jsx(Delete, {})
|
|
450
589
|
}
|
|
451
590
|
),
|
|
452
|
-
/* @__PURE__ */ jsxs(
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
591
|
+
/* @__PURE__ */ jsxs(
|
|
592
|
+
Dropdown2,
|
|
593
|
+
{
|
|
594
|
+
selection: true,
|
|
595
|
+
open: menuIsOpen,
|
|
596
|
+
onOpenChange: setMenuIsOpen,
|
|
597
|
+
children: [
|
|
598
|
+
/* @__PURE__ */ jsx(
|
|
599
|
+
Dropdown2.Trigger,
|
|
600
|
+
{
|
|
601
|
+
variant: "ghost",
|
|
602
|
+
className: "border-x-default rounded-none",
|
|
603
|
+
size: "large",
|
|
604
|
+
children: /* @__PURE__ */ jsxs("span", { className: "flex-1", children: [
|
|
605
|
+
actualZoomPercent,
|
|
606
|
+
"%"
|
|
607
|
+
] })
|
|
608
|
+
}
|
|
609
|
+
),
|
|
610
|
+
/* @__PURE__ */ jsxs(Dropdown2.Content, { children: [
|
|
611
|
+
/* @__PURE__ */ jsx(
|
|
612
|
+
Dropdown2.Item,
|
|
613
|
+
{
|
|
614
|
+
onMouseUp: () => handleZoomMenuItemClick(zoomRef.current + ZOOM_STEP),
|
|
615
|
+
shortcut: {
|
|
616
|
+
keys: "+",
|
|
617
|
+
modifier: "command"
|
|
618
|
+
},
|
|
619
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomIn })
|
|
620
|
+
}
|
|
621
|
+
),
|
|
622
|
+
/* @__PURE__ */ jsx(
|
|
623
|
+
Dropdown2.Item,
|
|
624
|
+
{
|
|
625
|
+
onMouseUp: () => handleZoomMenuItemClick(zoomRef.current - ZOOM_STEP),
|
|
626
|
+
shortcut: {
|
|
627
|
+
keys: "-",
|
|
628
|
+
modifier: "command"
|
|
629
|
+
},
|
|
630
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomOut })
|
|
631
|
+
}
|
|
632
|
+
),
|
|
633
|
+
/* @__PURE__ */ jsx(
|
|
634
|
+
Dropdown2.Item,
|
|
635
|
+
{
|
|
636
|
+
selected: actualZoomPercent === 50,
|
|
637
|
+
onMouseUp: () => setActualZoomPercent(50),
|
|
638
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomTo50 })
|
|
639
|
+
}
|
|
640
|
+
),
|
|
641
|
+
/* @__PURE__ */ jsx(
|
|
642
|
+
Dropdown2.Item,
|
|
643
|
+
{
|
|
644
|
+
selected: actualZoomPercent === 100,
|
|
645
|
+
onMouseUp: () => setActualZoomPercent(100),
|
|
646
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomTo100 })
|
|
647
|
+
}
|
|
648
|
+
),
|
|
649
|
+
/* @__PURE__ */ jsx(
|
|
650
|
+
Dropdown2.Item,
|
|
651
|
+
{
|
|
652
|
+
selected: actualZoomPercent === 200,
|
|
653
|
+
onMouseUp: () => setActualZoomPercent(200),
|
|
654
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomTo200 })
|
|
655
|
+
}
|
|
656
|
+
),
|
|
657
|
+
/* @__PURE__ */ jsx(Dropdown2.Divider, {}),
|
|
658
|
+
/* @__PURE__ */ jsx(
|
|
659
|
+
Dropdown2.Item,
|
|
660
|
+
{
|
|
661
|
+
onMouseUp: () => {
|
|
662
|
+
fitToView();
|
|
663
|
+
},
|
|
664
|
+
shortcut: {
|
|
665
|
+
keys: "1",
|
|
666
|
+
modifier: "command"
|
|
667
|
+
},
|
|
668
|
+
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.fitToScreen })
|
|
669
|
+
}
|
|
670
|
+
)
|
|
462
671
|
] })
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
/* @__PURE__ */ jsx(
|
|
467
|
-
Dropdown2.Item,
|
|
468
|
-
{
|
|
469
|
-
onMouseUp: () => handleZoomMenuItemClick(zoomRef.current + ZOOM_STEP),
|
|
470
|
-
shortcut: {
|
|
471
|
-
keys: "+",
|
|
472
|
-
modifier: "command"
|
|
473
|
-
},
|
|
474
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomIn })
|
|
475
|
-
}
|
|
476
|
-
),
|
|
477
|
-
/* @__PURE__ */ jsx(
|
|
478
|
-
Dropdown2.Item,
|
|
479
|
-
{
|
|
480
|
-
onMouseUp: () => handleZoomMenuItemClick(zoomRef.current - ZOOM_STEP),
|
|
481
|
-
shortcut: {
|
|
482
|
-
keys: "-",
|
|
483
|
-
modifier: "command"
|
|
484
|
-
},
|
|
485
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomOut })
|
|
486
|
-
}
|
|
487
|
-
),
|
|
488
|
-
/* @__PURE__ */ jsx(
|
|
489
|
-
Dropdown2.Item,
|
|
490
|
-
{
|
|
491
|
-
selected: zoomRef.current === 0.5,
|
|
492
|
-
onMouseUp: () => handleZoomMenuItemClick(0.5),
|
|
493
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomTo50 })
|
|
494
|
-
}
|
|
495
|
-
),
|
|
496
|
-
/* @__PURE__ */ jsx(
|
|
497
|
-
Dropdown2.Item,
|
|
498
|
-
{
|
|
499
|
-
selected: zoomRef.current === 1,
|
|
500
|
-
onMouseUp: () => handleZoomMenuItemClick(1),
|
|
501
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomTo100 })
|
|
502
|
-
}
|
|
503
|
-
),
|
|
504
|
-
/* @__PURE__ */ jsx(
|
|
505
|
-
Dropdown2.Item,
|
|
506
|
-
{
|
|
507
|
-
selected: zoomRef.current === 2,
|
|
508
|
-
onMouseUp: () => handleZoomMenuItemClick(2),
|
|
509
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.zoomTo200 })
|
|
510
|
-
}
|
|
511
|
-
),
|
|
512
|
-
/* @__PURE__ */ jsx(Dropdown2.Divider, {}),
|
|
513
|
-
/* @__PURE__ */ jsx(
|
|
514
|
-
Dropdown2.Item,
|
|
515
|
-
{
|
|
516
|
-
onMouseUp: () => {
|
|
517
|
-
fitToView();
|
|
518
|
-
},
|
|
519
|
-
shortcut: {
|
|
520
|
-
keys: "1",
|
|
521
|
-
modifier: "command"
|
|
522
|
-
},
|
|
523
|
-
children: /* @__PURE__ */ jsx("span", { className: "flex-1", children: defaultText.fitToScreen })
|
|
524
|
-
}
|
|
525
|
-
)
|
|
526
|
-
] })
|
|
527
|
-
] }),
|
|
672
|
+
]
|
|
673
|
+
}
|
|
674
|
+
),
|
|
528
675
|
/* @__PURE__ */ jsx(
|
|
529
676
|
IconButton,
|
|
530
677
|
{
|
|
@@ -2,12 +2,17 @@ interface Position {
|
|
|
2
2
|
x: number;
|
|
3
3
|
y: number;
|
|
4
4
|
}
|
|
5
|
+
interface ZoomAtPointParams {
|
|
6
|
+
newZoom: number;
|
|
7
|
+
newPosition: Position;
|
|
8
|
+
}
|
|
5
9
|
interface WheelHandlerOptions {
|
|
6
10
|
minZoom?: number;
|
|
7
11
|
maxZoom?: number;
|
|
8
12
|
zoomStep?: number;
|
|
9
13
|
onZoom?: (newZoom: number) => void;
|
|
10
14
|
onPan?: (newPosition: Position) => void;
|
|
15
|
+
onZoomAtPoint?: (params: ZoomAtPointParams) => void;
|
|
11
16
|
}
|
|
12
17
|
/**
|
|
13
18
|
* 用于处理鼠标滚轮和触摸板手势的自定义Hook
|
|
@@ -16,7 +21,7 @@ interface WheelHandlerOptions {
|
|
|
16
21
|
* @param positionRef 当前位置的ref
|
|
17
22
|
* @param options 配置选项
|
|
18
23
|
*/
|
|
19
|
-
export declare function useWheelHandler(targetRef: React.RefObject<HTMLElement>, zoomRef: React.RefObject<number>, positionRef: React.RefObject<Position>, options?: WheelHandlerOptions): {
|
|
24
|
+
export declare function useWheelHandler(targetRef: React.RefObject<HTMLElement | null>, zoomRef: React.RefObject<number>, positionRef: React.RefObject<Position>, options?: WheelHandlerOptions): {
|
|
20
25
|
isMac: boolean;
|
|
21
26
|
isCmdPressed: import('react').MutableRefObject<boolean>;
|
|
22
27
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useRef, useCallback, useEffect } from "react";
|
|
2
2
|
function useWheelHandler(targetRef, zoomRef, positionRef, options = {}) {
|
|
3
|
-
const { minZoom = 0.01, maxZoom = 10, zoomStep = 0.1, onZoom, onPan } = options;
|
|
3
|
+
const { minZoom = 0.01, maxZoom = 10, zoomStep = 0.1, onZoom, onPan, onZoomAtPoint } = options;
|
|
4
4
|
const isMac = useRef(
|
|
5
5
|
typeof navigator !== "undefined" && navigator.platform.toUpperCase().indexOf("MAC") >= 0
|
|
6
6
|
);
|
|
@@ -9,14 +9,32 @@ function useWheelHandler(targetRef, zoomRef, positionRef, options = {}) {
|
|
|
9
9
|
(event) => {
|
|
10
10
|
event.preventDefault();
|
|
11
11
|
event.stopPropagation();
|
|
12
|
-
if (
|
|
12
|
+
if (zoomRef.current === void 0 || zoomRef.current === null || !positionRef.current) return;
|
|
13
13
|
const isPreciseEvent = event.deltaMode === 0;
|
|
14
14
|
const hasDeltaX = Math.abs(event.deltaX) > 0;
|
|
15
15
|
const isZoomModifier = isMac.current && event.metaKey || !isMac.current && event.ctrlKey;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
16
|
+
const isPinchZoom = event.ctrlKey && isPreciseEvent && !event.metaKey;
|
|
17
|
+
if (isZoomModifier || isPinchZoom) {
|
|
18
|
+
const oldZoom = zoomRef.current;
|
|
19
|
+
let newZoom;
|
|
20
|
+
const sensitivity = isPinchZoom ? 8e-3 : 3e-3;
|
|
21
|
+
const delta = event.deltaY;
|
|
22
|
+
newZoom = oldZoom * Math.exp(-delta * sensitivity);
|
|
23
|
+
newZoom = Math.max(minZoom, Math.min(maxZoom, newZoom));
|
|
24
|
+
const target = targetRef.current;
|
|
25
|
+
if (onZoomAtPoint && target) {
|
|
26
|
+
const rect = target.getBoundingClientRect();
|
|
27
|
+
const mouseX = event.clientX - rect.left - rect.width / 2;
|
|
28
|
+
const mouseY = event.clientY - rect.top - rect.height / 2;
|
|
29
|
+
const zoomRatio = newZoom / oldZoom;
|
|
30
|
+
const currentX = positionRef.current.x;
|
|
31
|
+
const currentY = positionRef.current.y;
|
|
32
|
+
const newPosition = {
|
|
33
|
+
x: mouseX - (mouseX - currentX) * zoomRatio,
|
|
34
|
+
y: mouseY - (mouseY - currentY) * zoomRatio
|
|
35
|
+
};
|
|
36
|
+
onZoomAtPoint({ newZoom, newPosition });
|
|
37
|
+
} else if (onZoom) {
|
|
20
38
|
onZoom(newZoom);
|
|
21
39
|
}
|
|
22
40
|
} else if (isPreciseEvent && hasDeltaX) {
|
|
@@ -48,7 +66,7 @@ function useWheelHandler(targetRef, zoomRef, positionRef, options = {}) {
|
|
|
48
66
|
}
|
|
49
67
|
}
|
|
50
68
|
},
|
|
51
|
-
[minZoom, maxZoom,
|
|
69
|
+
[minZoom, maxZoom, onZoom, onPan, onZoomAtPoint, targetRef]
|
|
52
70
|
);
|
|
53
71
|
const handleKeyDown = useCallback((e) => {
|
|
54
72
|
if (isMac.current && e.metaKey || !isMac.current && e.ctrlKey) {
|