Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to control alignment in fitSelection() and "zoom in" tool + typescript support #226

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
| customToolbar | - | Component | Override toolbar component |
| toolbarProps | {} | Object | Toolbar settings |
| toolbarProps.position | `right` | one of `none`, `top`, `right`, `bottom`, `left` | Toolbar position |
| toolbarProps.SVGAlignX | `left` | one of `left`, `center`, `right` | X Alignment used for "Fit to Viewer" action |
| toolbarProps.SVGAlignY | `top` | one of `top`, `center`, `bottom` | Y Alignment used for "Fit to Viewer" action |
| toolbarProps.SVGAlignX | `left` | one of `left`, `center`, `right`, `cover` | X Alignment used for "Fit to Viewer" and "Zoom in" actions |
| toolbarProps.SVGAlignY | `top` | one of `top`, `center`, `bottom`, `cover` | Y Alignment used for "Fit to Viewer" and "Zoom in" actions |
| toolbarProps.activeToolColor | `#1CA6FC` | String | Color of active and hovered tool icons |

\* handler available only with the tool `none` or `auto`
Expand All @@ -57,8 +57,8 @@
|-----|------|
| `pan(SVGDeltaX, SVGDeltaY)` | Apply a pan |
| `zoom(SVGPointX, SVGPointY, scaleFactor)` | Zoom in or out the SVG |
| `fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight)`| Fit an SVG area to viewer |
| `fitToViewer(SVGAlignX = "left", SVGAlignY = "top")` | Fit all SVG to Viewer (`SVGAlignX`: one of `left`, `center`, `right`, `SVGAlignY`: one of `top`, `center`, `bottom`) |
| `fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX = "left", SVGAlignY = "top")`| Fit an SVG area to viewer (`SVGAlignX`: one of `left`, `center`, `right`, `cover`, `SVGAlignY`: one of `top`, `center`, `bottom`, `cover`) |
| `fitToViewer(SVGAlignX = "left", SVGAlignY = "top")` | Fit all SVG to Viewer (`SVGAlignX`: one of `left`, `center`, `right`, `cover`, `SVGAlignY`: one of `top`, `center`, `bottom`, `cover`) |
| `setPointOnViewerCenter(SVGPointX, SVGPointY, zoomLevel)`| Set a point on Viewer center |
| `reset()` | Reset Viewer view to default |
| `zoomOnViewerCenter(scaleFactor)` | Zoom SVG on center |
Expand Down
296 changes: 296 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
import * as React from "react";

// String constants:
export const MODE_IDLE = "idle";
export const MODE_PANNING = "panning";
export const MODE_ZOOMING = "zooming";

export const TOOL_AUTO = "auto";
export const TOOL_NONE = "none";
export const TOOL_PAN = "pan";
export const TOOL_ZOOM_IN = "zoom-in";
export const TOOL_ZOOM_OUT = "zoom-out";

export const POSITION_NONE = "none";
export const POSITION_TOP = "top";
export const POSITION_RIGHT = "right";
export const POSITION_BOTTOM = "bottom";
export const POSITION_LEFT = "left";

export const ALIGN_CENTER = "center";
export const ALIGN_LEFT = "left";
export const ALIGN_RIGHT = "right";
export const ALIGN_TOP = "top";
export const ALIGN_BOTTOM = "bottom";
export const ALIGN_COVER = 'cover';

export type Mode = typeof MODE_IDLE | typeof MODE_PANNING | typeof MODE_ZOOMING;

export interface Value {
version: 2;
mode: Mode;
focus: boolean;
a: number;
b: number;
c: number;
d: number;
e: number;
f: number;
viewerWidth: number;
viewerHeight: number;
SVGWidth: number;
SVGHeight: number;
startX?: number | null | undefined;
startY?: number | null | undefined;
endX?: number | null | undefined;
endY?: number | null | undefined;
miniatureOpen: boolean;
}

export type Tool = typeof TOOL_AUTO | typeof TOOL_NONE | typeof TOOL_PAN | typeof TOOL_ZOOM_IN | typeof TOOL_ZOOM_OUT;
export type ToolbarPosition =
| typeof POSITION_NONE
| typeof POSITION_TOP
| typeof POSITION_RIGHT
| typeof POSITION_BOTTOM
| typeof POSITION_LEFT;

export interface OptionalProps {
// default tool
defaultTool: Exclude<Tool, typeof TOOL_AUTO>;

// background of the viewer
background: string;

// background of the svg
SVGBackground: string;

// CSS style of the Viewer
style: object;

// className of the Viewer
className: string;

// detect zoom operation performed trough pinch gesture or mouse scroll
detectWheel: boolean;

// perform PAN if the mouse is on viewer border
detectAutoPan: boolean;

detectPinchGesture: boolean;

toolbarProps: {
position?: ToolbarPosition | undefined;
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | undefined;
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | undefined;
};

customMiniature: React.ReactElement | React.ComponentType;
miniatureProps: {
position: typeof POSITION_NONE | typeof POSITION_RIGHT | typeof POSITION_LEFT;
background: string;
width: number;
height: number;
};

// Note: The `T` type parameter is the type of the `target` of the event:
// handler click
onClick<T>(event: ViewerMouseEvent<T>): void;

// handler double click
onDoubleClick<T>(event: ViewerMouseEvent<T>): void;

// handler mouseup
onMouseUp<T>(event: ViewerMouseEvent<T>): void;

// handler mousemove
onMouseMove<T>(event: ViewerMouseEvent<T>): void;

// handler mousedown
onMouseDown<T>(event: ViewerMouseEvent<T>): void;

// handler zoom level changed
onZoom<T>(event: ViewerMouseEvent<T>): void;

// handler pan action performed
onPan<T>(event: ViewerMouseEvent<T>): void;

// if disabled the user can move the image outside the viewer
preventPanOutside: boolean;

// how much scale in or out
scaleFactor: number;

// how much scale in or out on mouse wheel (requires detectWheel enabled)
scaleFactorOnWheel: number;

// maximum amount of scale a user can zoom in to
scaleFactorMax: number;

// minimum amount of a scale a user can zoom out of
scaleFactorMin: number;

// modifier keys //https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/getModifierState
modifierKeys: string[];

// Turn off zoom on double click
disableDoubleClickZoomWithToolAuto: boolean;

// override default toolbar component
// TODO: specify function type more clearly
customToolbar: React.Component<any> | React.FunctionComponent<any>;

// How about touch events? They are in README but not in `propTypes`.
}

export interface RequiredProps {
children: React.ReactElement;
// width of the viewer displayed on screen
width: number;
// height of the viewer displayed on screen
height: number;
// current active tool (TOOL_NONE, TOOL_PAN, TOOL_ZOOM_IN, TOOL_ZOOM_OUT)
tool: Tool;
// value of the viewer (current point of view)
value: Value | null;
// handler tool changed
onChangeTool(tool: Tool): void;
// handler something changed
onChangeValue(value: Value): void;

// accept only one node SVG
// TODO: Figure out how to constrain `children` or maybe just leave it commented out
// because `children` is already implicit props
// children: () => any;
}

export interface UncontrolledExtraOptionalProps {
// current active tool (TOOL_NONE, TOOL_PAN, TOOL_ZOOM_IN, TOOL_ZOOM_OUT)
tool: Tool;
// value of the viewer (current point of view)
value: Value | null;
// handler tool changed
onChangeTool(tool: Tool): void;
// handler something changed
onChangeValue(value: Value): void;
}

export interface UncontrolledRequiredProps {
children: React.ReactElement;
// width of the viewer displayed on screen
width: number;
// height of the viewer displayed on screen
height: number;
}

export type Props = RequiredProps & Partial<OptionalProps>;

export class ReactSVGPanZoom extends React.Component<Props> {
pan(SVGDeltaX: number, SVGDeltaY: number): void;
zoom(SVGPointX: number, SVGPointY: number, scaleFactor: number): void;
fitSelection(
selectionSVGPointX: number,
selectionSVGPointY: number,
selectionWidth: number,
selectionHeight: number,
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | typeof ALIGN_COVER | undefined,
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | typeof ALIGN_COVER | undefined,
): void;
fitToViewer(
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | typeof ALIGN_COVER | undefined,
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | typeof ALIGN_COVER | undefined,
): void;
setPointOnViewerCenter(SVGPointX: number, SVGPointY: number, zoomLevel: number): void;
reset(): void;
zoomOnViewerCenter(scaleFactor: number): void;
getValue(): Value;
setValue(value: Value): void;
getTool(): Tool;
setTool(tool: Tool): void;
}

export type UncontrolledProps =
& UncontrolledRequiredProps
& Partial<OptionalProps>
& Partial<UncontrolledExtraOptionalProps>;

export class UncontrolledReactSVGPanZoom extends React.Component<UncontrolledProps> {
pan(SVGDeltaX: number, SVGDeltaY: number): void;
zoom(SVGPointX: number, SVGPointY: number, scaleFactor: number): void;
fitSelection(
selectionSVGPointX: number,
selectionSVGPointY: number,
selectionWidth: number,
selectionHeight: number,
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | typeof ALIGN_COVER | undefined,
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | typeof ALIGN_COVER | undefined,
): void;
fitToViewer(
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | typeof ALIGN_COVER | undefined,
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | typeof ALIGN_COVER | undefined,
): void;
setPointOnViewerCenter(SVGPointX: number, SVGPointY: number, zoomLevel: number): void;
reset(): void;
zoomOnViewerCenter(scaleFactor: number): void;
getValue(): Value;
setValue(value: Value): void;
getTool(): Tool;
setTool(tool: Tool): void;
}

export interface Point {
x: number;
y: number;
}

export interface ViewerMouseEvent<T> {
originalEvent: React.MouseEvent<T>;
SVGViewer: SVGSVGElement;
point: Point;
x: number;
y: number;
scaleFactor: number;
translationX: number;
translationY: number;
preventDefault(): void;
stopPropagation(): void;
}

export interface ViewerTouchEvent<T> {
originalEvent: React.TouchEvent<T>;
SVGViewer: SVGSVGElement;
points: Point[];
changedPoints: Point[];
scaleFactor: number;
translationX: number;
translationY: number;
preventDefault(): void;
stopPropagation(): void;
}

// Utility functions exposed:
export function pan(value: Value, SVGDeltaX: number, SVGDeltaY: number, panLimit?: number): Value;

export function zoom(value: Value, SVGPointX: number, SVGPointY: number, scaleFactor: number): Value;

export function fitSelection(
value: Value,
selectionSVGPointX: number,
selectionSVGPointY: number,
selectionWidth: number,
selectionHeight: number,
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | typeof ALIGN_COVER | undefined,
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | typeof ALIGN_COVER | undefined,
): Value;

export function fitToViewer(
value: Value,
SVGAlignX?: typeof ALIGN_CENTER | typeof ALIGN_LEFT | typeof ALIGN_RIGHT | typeof ALIGN_COVER | undefined,
SVGAlignY?: typeof ALIGN_CENTER | typeof ALIGN_TOP | typeof ALIGN_BOTTOM | typeof ALIGN_COVER | undefined,
): Value;

export function zoomOnViewerCenter(value: Value, scaleFactor: number): Value;

export function setPointOnViewerCenter(value: Value, SVGPointX: number, SVGPointY: number, zoomLevel: number): Value;

export function reset(value: Value): Value;
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"module": "./build-es/index.js",
"unpkg": "./build-umd/react-svg-pan-zoom.min.js",
"jsnext:main": "./build-es/index.js",
"types": "./index.d.ts",
"scripts": {
"start": "npm run website:start",
"build": "npm run clean && npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:umd_min",
Expand All @@ -24,7 +25,8 @@
"files": [
"*.md",
"build-*",
"src"
"src",
"*.d.ts"
],
"repository": {
"type": "git",
Expand Down
6 changes: 3 additions & 3 deletions src/features/interactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export function onMouseMove(event, ViewerDOM, tool, value, props, coords = null)
switch (tool) {
case TOOL_ZOOM_IN:
if (value.mode === MODE_ZOOMING)
nextValue = forceExit ? stopZooming(value, x, y, props.scaleFactor, props) : updateZooming(value, x, y);
nextValue = forceExit ? stopZooming(value, x, y, props.scaleFactor, props.toolbarProps?.SVGAlignX, props.toolbarProps?.SVGAlignY) : updateZooming(value, x, y);
break;

case TOOL_AUTO:
Expand All @@ -73,12 +73,12 @@ export function onMouseUp(event, ViewerDOM, tool, value, props, coords = null) {
switch (tool) {
case TOOL_ZOOM_OUT:
if (value.mode === MODE_ZOOMING)
nextValue = stopZooming(value, x, y, 1 / props.scaleFactor, props);
nextValue = stopZooming(value, x, y, 1 / props.scaleFactor, props.toolbarProps?.SVGAlignX, props.toolbarProps?.SVGAlignY);
break;

case TOOL_ZOOM_IN:
if (value.mode === MODE_ZOOMING)
nextValue = stopZooming(value, x, y, props.scaleFactor, props);
nextValue = stopZooming(value, x, y, props.scaleFactor, props.toolbarProps?.SVGAlignX, props.toolbarProps?.SVGAlignY);
break;

case TOOL_AUTO:
Expand Down
Loading