@scaleflex/crop 2.0.1

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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +452 -0
  3. package/dist/a11y/aria.d.ts +5 -0
  4. package/dist/a11y/keyboard.d.ts +13 -0
  5. package/dist/animation/lerp.d.ts +15 -0
  6. package/dist/animation/spring.d.ts +15 -0
  7. package/dist/canvas/bleed-layer.d.ts +6 -0
  8. package/dist/canvas/crop-frame.d.ts +32 -0
  9. package/dist/canvas/grid-layer.d.ts +7 -0
  10. package/dist/canvas/hit-test.d.ts +10 -0
  11. package/dist/canvas/image-layer.d.ts +28 -0
  12. package/dist/canvas/overlay-layer.d.ts +6 -0
  13. package/dist/canvas/renderer.d.ts +34 -0
  14. package/dist/chunks/sfx-crop-1LGASewd.cjs +353 -0
  15. package/dist/chunks/sfx-crop-CEe6OfTZ.js +2030 -0
  16. package/dist/core/config.d.ts +10 -0
  17. package/dist/core/crop-controller.d.ts +65 -0
  18. package/dist/core/types.d.ts +270 -0
  19. package/dist/define.cjs +1194 -0
  20. package/dist/define.d.ts +1 -0
  21. package/dist/define.js +1746 -0
  22. package/dist/elements/base.d.ts +17 -0
  23. package/dist/elements/icons.d.ts +21 -0
  24. package/dist/elements/parse-shapes.d.ts +13 -0
  25. package/dist/elements/popover-anchor.d.ts +20 -0
  26. package/dist/elements/sfx-crop-canvas.d.ts +24 -0
  27. package/dist/elements/sfx-crop-canvas.styles.d.ts +1 -0
  28. package/dist/elements/sfx-crop-rotate.d.ts +42 -0
  29. package/dist/elements/sfx-crop-rotate.styles.d.ts +5 -0
  30. package/dist/elements/sfx-crop-shapes.d.ts +67 -0
  31. package/dist/elements/sfx-crop-shapes.styles.d.ts +6 -0
  32. package/dist/elements/sfx-crop-toolbar.d.ts +64 -0
  33. package/dist/elements/sfx-crop-toolbar.styles.d.ts +7 -0
  34. package/dist/elements/sfx-crop-zoom.d.ts +66 -0
  35. package/dist/elements/sfx-crop-zoom.styles.d.ts +7 -0
  36. package/dist/elements/sfx-crop.d.ts +134 -0
  37. package/dist/elements/sfx-crop.styles.d.ts +9 -0
  38. package/dist/export/exporter.d.ts +19 -0
  39. package/dist/index.cjs +2 -0
  40. package/dist/index.d.ts +22 -0
  41. package/dist/index.js +65 -0
  42. package/dist/interactions/drag-crop.d.ts +10 -0
  43. package/dist/interactions/pinch-zoom.d.ts +14 -0
  44. package/dist/interactions/pointer-tracker.d.ts +29 -0
  45. package/dist/interactions/resize-handles.d.ts +13 -0
  46. package/dist/interactions/wheel-zoom.d.ts +12 -0
  47. package/dist/react/define-CVJd5aYk.cjs +1545 -0
  48. package/dist/react/define-t4Z6KaLY.js +2590 -0
  49. package/dist/react/index-B-csHwK2.cjs +2 -0
  50. package/dist/react/index-CktjrogS.js +1468 -0
  51. package/dist/react/index.cjs +2 -0
  52. package/dist/react/index.d.ts +21 -0
  53. package/dist/react/index.js +10 -0
  54. package/dist/react/sfx-crop.d.ts +86 -0
  55. package/dist/react/use-sfx-crop-controller.d.ts +74 -0
  56. package/dist/react/use-sfx-crop.d.ts +31 -0
  57. package/dist/styles/shared.css.d.ts +20 -0
  58. package/dist/transforms/constrain.d.ts +68 -0
  59. package/dist/transforms/matrix.d.ts +23 -0
  60. package/dist/transforms/transform-state.d.ts +12 -0
  61. package/dist/utils/events.d.ts +16 -0
  62. package/dist/utils/math.d.ts +12 -0
  63. package/package.json +108 -0
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-B-csHwK2.cjs");exports.DEFAULT_CONFIG=e.DEFAULT_CONFIG;exports.SfxCrop=e.SfxCrop;exports.createCropController=e.createCropController;exports.mergeConfig=e.mergeConfig;exports.useSfxCrop=e.useSfxCrop;exports.useSfxCropController=e.useSfxCropController;
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * React entry for `@scaleflex/crop/react`.
3
+ *
4
+ * Exports:
5
+ * - `SfxCrop` — forwardRef component rendering `<sfx-crop>`
6
+ * - `useSfxCrop` — hook variant for consumers that render the element manually
7
+ * - `SfxCropElement`, `SfxCropProps`, `SfxCropSaveDetail` — types
8
+ *
9
+ * The wrapper dynamically imports `../define` on module load so the custom
10
+ * element auto-registers whenever this module is evaluated in the browser.
11
+ * SSR-safe: the import is guarded behind a `typeof customElements` check.
12
+ */
13
+ export { SfxCrop } from './sfx-crop';
14
+ export type { SfxCropProps, SfxCropElement, SfxCropSaveDetail } from './sfx-crop';
15
+ export { useSfxCrop } from './use-sfx-crop';
16
+ export type { UseSfxCropReturn } from './use-sfx-crop';
17
+ export { useSfxCropController } from './use-sfx-crop-controller';
18
+ export type { UseSfxCropControllerOptions, UseSfxCropControllerReturn, CropControllerState, CropControllerActions, CropControllerApi, } from './use-sfx-crop-controller';
19
+ export { createCropController, DEFAULT_CONFIG, mergeConfig } from '../index';
20
+ export type { CropController, CropControllerOptions, CropControllerCallbacks, SfxCropConfig, } from '../index';
21
+ export type { TransformState, TransformParams, CropShapeName, CropShape, CropRect, NormalizedRect, HandlePosition, Point, Size, CropIconOverrides, } from '../core/types';
@@ -0,0 +1,10 @@
1
+ import { D as e, S as s, c as C, m as a, u as f, b as p } from "./index-CktjrogS.js";
2
+ export {
3
+ e as DEFAULT_CONFIG,
4
+ s as SfxCrop,
5
+ C as createCropController,
6
+ a as mergeConfig,
7
+ f as useSfxCrop,
8
+ p as useSfxCropController
9
+ };
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,86 @@
1
+ import { CSSProperties } from 'react';
2
+ import { SfxCropElement } from '../elements/sfx-crop';
3
+ import { CropShapeName, CropRect, TransformState, TransformParams, CropIconOverrides } from '../core/types';
4
+ export type { SfxCropElement };
5
+ export interface SfxCropSaveDetail {
6
+ blob: Blob;
7
+ dataURL: string;
8
+ params: TransformParams;
9
+ }
10
+ export interface SfxCropProps {
11
+ src?: string;
12
+ /** `'classic'` (default) or `'fixed'` — see `<sfx-crop>` `variant`. */
13
+ variant?: 'classic' | 'fixed';
14
+ cropShape?: CropShapeName;
15
+ theme?: 'light' | 'dark';
16
+ initialCrop?: CropRect | null;
17
+ initialRotation?: number;
18
+ initialScale?: number;
19
+ minScale?: number;
20
+ maxScale?: number;
21
+ minCropSize?: number;
22
+ availableShapes?: CropShapeName[];
23
+ /** Per-slot icon overrides — see `CropIconOverrides` in the core types. */
24
+ icons?: CropIconOverrides;
25
+ handleSize?: number;
26
+ handleColor?: string;
27
+ borderRadius?: number;
28
+ overlayColor?: string;
29
+ outputType?: string;
30
+ outputQuality?: number;
31
+ maxOutputWidth?: number;
32
+ maxOutputHeight?: number;
33
+ showGrid?: boolean | 'interaction';
34
+ showToolbar?: boolean;
35
+ showRotateSlider?: boolean;
36
+ showZoomSlider?: boolean;
37
+ showShapeSelector?: boolean;
38
+ showRotateButton?: boolean;
39
+ showFlipButton?: boolean;
40
+ toolbarPosition?: 'top' | 'bottom';
41
+ showBleedMargin?: boolean;
42
+ bleedMarginSize?: number;
43
+ bleedMarginColor?: string;
44
+ enableAnimations?: boolean;
45
+ animationSpeed?: number;
46
+ keyboard?: boolean;
47
+ pinchZoom?: boolean;
48
+ wheelZoom?: boolean;
49
+ onReady?: (detail: {
50
+ element: SfxCropElement;
51
+ }) => void;
52
+ onImageLoad?: (detail: {
53
+ image: HTMLImageElement;
54
+ }) => void;
55
+ onChange?: (state: TransformState) => void;
56
+ onCropChange?: (crop: CropRect) => void;
57
+ onSave?: (detail: SfxCropSaveDetail) => void;
58
+ onCancel?: () => void;
59
+ onError?: (detail: {
60
+ error: Error;
61
+ }) => void;
62
+ className?: string;
63
+ style?: CSSProperties;
64
+ id?: string;
65
+ }
66
+ /**
67
+ * `<SfxCrop>` — React wrapper around the `<sfx-crop>` custom element.
68
+ *
69
+ * Pattern follows `@scaleflex/uploader`'s React wrapper: hand-rolled
70
+ * `forwardRef` + dynamic `import('../define')` at module load to auto-register
71
+ * the element, + `useEffect` bridges from CustomEvent to prop callbacks.
72
+ *
73
+ * Stale-closure avoidance: the latest props sit in a mutable ref updated on
74
+ * every render. The event-bridge `useEffect` attaches listeners once and
75
+ * reads `cbRef.current.on*` at fire time — no re-subscription on re-render.
76
+ *
77
+ * Prop sync: runs on every render without a deps array. Operations are
78
+ * idempotent (Lit compares before assigning), so the cost is a cheap walk
79
+ * of FORWARDED_PROPS.
80
+ *
81
+ * The ref is the bare element. The factory runs after React's commit phase,
82
+ * so `ref.current` is non-null once a parent component can actually read it.
83
+ * If you need to access methods before the first commit (e.g. during render),
84
+ * guard with `ref.current?.method?.()` or wait for `sfx-crop-ready`.
85
+ */
86
+ export declare const SfxCrop: import('react').ForwardRefExoticComponent<SfxCropProps & import('react').RefAttributes<SfxCropElement>>;
@@ -0,0 +1,74 @@
1
+ import { CropController } from '../core/crop-controller';
2
+ import { SfxCropConfig, TransformState, TransformParams, CropRect, CropShapeName } from '../core/types';
3
+ /**
4
+ * Headless options for {@link useSfxCropController}. Most fields mirror the
5
+ * built-in element's attributes, minus UI toggles that are irrelevant when
6
+ * the consumer provides their own UI.
7
+ */
8
+ export type UseSfxCropControllerOptions = Partial<SfxCropConfig>;
9
+ /** Reactive snapshot exposed by the hook. */
10
+ export interface CropControllerState {
11
+ /** Current editor transform (rotation, flip, scale, pan, crop). */
12
+ state: TransformState;
13
+ cropRect: CropRect;
14
+ cropShape: CropShapeName;
15
+ scale: number;
16
+ rotation: number;
17
+ loading: boolean;
18
+ error: string | null;
19
+ /** `true` once the editor has a decoded image and the render loop is live. */
20
+ ready: boolean;
21
+ }
22
+ /** Imperative actions — stable identity across renders. */
23
+ export interface CropControllerActions {
24
+ loadImage(src: string): Promise<void>;
25
+ rotateLeft(): void;
26
+ flipHorizontal(): void;
27
+ setRotation(deg: number): void;
28
+ setScale(scale: number): void;
29
+ setCropShape(shape: CropShapeName): void;
30
+ setCropRect(rect: CropRect): void;
31
+ reset(): void;
32
+ }
33
+ /** Export / query surface — stable identity across renders. */
34
+ export interface CropControllerApi {
35
+ toCanvas(): HTMLCanvasElement | null;
36
+ toBlob(type?: string, quality?: number): Promise<Blob | null>;
37
+ toDataURL(type?: string, quality?: number): string | null;
38
+ toTransformParams(): TransformParams | null;
39
+ /** Underlying controller handle (null until both refs are attached). */
40
+ getController(): CropController | null;
41
+ }
42
+ export interface UseSfxCropControllerReturn extends CropControllerState {
43
+ /** Attach to your `<canvas>` node. */
44
+ canvasRef: React.RefObject<HTMLCanvasElement | null>;
45
+ /** Attach to a sizing box (e.g. a `<div>` with `max-width`/`max-height`). */
46
+ containerRef: React.RefObject<HTMLElement | null>;
47
+ actions: CropControllerActions;
48
+ api: CropControllerApi;
49
+ }
50
+ /**
51
+ * Headless React hook — wraps {@link createCropController} so a consumer can
52
+ * drop a `<canvas>` + sizing container anywhere in their tree and wire up
53
+ * their own UI (their design-system buttons, sliders, modals, etc.). The
54
+ * hook never mounts `<sfx-crop>`; there is no Lit, no shadow DOM, no
55
+ * built-in toolbar.
56
+ *
57
+ * Usage:
58
+ * ```tsx
59
+ * const { canvasRef, containerRef, state, actions, api } = useSfxCropController({
60
+ * src: '/photo.jpg',
61
+ * cropShape: '16:9',
62
+ * });
63
+ *
64
+ * return (
65
+ * <div ref={containerRef} style={{ maxWidth: 1200, maxHeight: 640 }}>
66
+ * <canvas ref={canvasRef} style={{ width: '100%', height: '100%' }} />
67
+ * <button onClick={actions.rotateLeft}>Rotate</button>
68
+ * <input type="range" min={0.5} max={5} step={0.01}
69
+ * value={state.scale} onChange={(e) => actions.setScale(+e.target.value)} />
70
+ * </div>
71
+ * );
72
+ * ```
73
+ */
74
+ export declare function useSfxCropController(options?: UseSfxCropControllerOptions): UseSfxCropControllerReturn;
@@ -0,0 +1,31 @@
1
+ import { SfxCropElement } from '../elements/sfx-crop';
2
+ import { CropRect, CropShapeName, TransformState, TransformParams } from '../core/types';
3
+ export interface UseSfxCropReturn {
4
+ /** Attach this ref to your `<sfx-crop ref={ref}>` (or the React `SfxCrop`). */
5
+ ref: React.RefObject<SfxCropElement | null>;
6
+ /** Fires after the editor has loaded the image and the renderer is live. */
7
+ ready: boolean;
8
+ loadImage(src: string): Promise<void>;
9
+ rotateLeft(): void;
10
+ flipHorizontal(): void;
11
+ setRotation(deg: number): void;
12
+ setScale(scale: number): void;
13
+ setCropShape(shape: CropShapeName): void;
14
+ setCropRect(rect: CropRect): void;
15
+ getCropRect(): CropRect | null;
16
+ getTransformState(): TransformState | null;
17
+ reset(): void;
18
+ toCanvas(): HTMLCanvasElement | null;
19
+ toBlob(type?: string, quality?: number): Promise<Blob | null>;
20
+ toDataURL(type?: string, quality?: number): string | null;
21
+ toTransformParams(): TransformParams | null;
22
+ save(type?: string, quality?: number): Promise<void>;
23
+ cancel(): void;
24
+ }
25
+ /**
26
+ * Hook variant of the React wrapper — returns a ref to bind plus stable
27
+ * callables for imperative operations. Prefer the `<SfxCrop>` component for
28
+ * declarative usage; reach for this hook when you need to render the element
29
+ * yourself or share imperative access across multiple components.
30
+ */
31
+ export declare function useSfxCrop(): UseSfxCropReturn;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Design tokens — sourced from the @scaleflex/ui-tw kit
3
+ * (packages/ui/src/styles/variables.css). Color values are exact OKLCH
4
+ * copies of the kit's --background / --foreground / --primary / etc., so
5
+ * a page that theme-embeds both <sfx-crop> and the ui-tw components
6
+ * shares a single palette. Override any token from light DOM, e.g.
7
+ * `<sfx-crop style="--sfx-cr-primary:oklch(0.6 0.18 280)">`.
8
+ *
9
+ * Light is the default; `theme="dark"` mirrors the kit's `:root.dark`.
10
+ * Tokens cascade through shadow boundaries via CSS custom-property
11
+ * inheritance, so sub-elements never redeclare them.
12
+ */
13
+ export declare const designTokens: import('lit').CSSResult;
14
+ export declare const baseStyles: import('lit').CSSResult;
15
+ export declare const spinKeyframes: import('lit').CSSResult;
16
+ export declare const toolbarEnterKeyframes: import('lit').CSSResult;
17
+ export declare const zoomEnterKeyframes: import('lit').CSSResult;
18
+ export declare const modalInKeyframes: import('lit').CSSResult;
19
+ /** Shared slider-thumb styling used by zoom + rotate inputs. */
20
+ export declare const sliderThumbStyles: import('lit').CSSResult;
@@ -0,0 +1,68 @@
1
+ import { CropRect, CropShapeName, TransformState } from '../core/types';
2
+ /**
3
+ * Parse a free-form aspect-ratio string like `"16:9"`, `"7:2"`, or `"11:8"`
4
+ * into a numeric ratio. Supports positive integers or decimals. Returns
5
+ * `null` if the input isn't a valid W:H pair — consumers can then treat it
6
+ * as a named shape (`'free'`, `'square'`, …) or reject it.
7
+ */
8
+ export declare function parseRatio(name: string): number | null;
9
+ /**
10
+ * Get the numeric aspect ratio for a crop shape. Handles the named shapes
11
+ * (`free`, `square`, `circle`, `rounded-rect`) and every built-in preset
12
+ * (`"16:9"`, etc.), plus any other `"W:H"` string a consumer passes in.
13
+ * Returns `null` for free-form (no ratio constraint).
14
+ */
15
+ export declare function getAspectRatio(shape: CropShapeName | string): number | null;
16
+ /** Clamp crop rect to stay within [0,1] image bounds. Spec section 8.3. */
17
+ export declare function clampCropToImage(crop: CropRect): CropRect;
18
+ /** Enforce aspect ratio on crop rect during resize. Spec section 8.3. */
19
+ export declare function enforceAspectRatio(crop: CropRect, ratio: number | null, handle: string, imageWidth: number, imageHeight: number, minSize: number): CropRect;
20
+ export declare function enforceMinSize(crop: CropRect, minSize: number, imageWidth: number, imageHeight: number): CropRect;
21
+ /** Compute minimum scale needed to cover the crop area at a given rotation. */
22
+ export declare function computeMinScale(imageWidth: number, imageHeight: number, canvasWidth: number, canvasHeight: number, rotation: number): number;
23
+ /**
24
+ * Cover constraint: keep the photo fully covering a target frame so the crop
25
+ * never exports transparent gaps. Returns the `scale` / `panX` / `panY` to use
26
+ * plus the (capped) minimum scale below which coverage breaks.
27
+ *
28
+ * The frame is given in container CSS px and may be **off-centre** (the classic
29
+ * movable crop rect) or the whole editor box (the fixed variant). The photo is
30
+ * always drawn centred in the container; pan is stored in container CSS px
31
+ * relative to the image centre, and the live draw places the image centre at
32
+ * `scale * pan` px from the container centre (see `image-layer.ts` —
33
+ * translate(center)→scale→translate(pan)). The math works in CSS px and divides
34
+ * the slack by `scale`.
35
+ *
36
+ * `drawW0` / `drawH0` are the photo's draw size **at scale 1** in container px —
37
+ * the caller passes the model that matches the actual render: cover-fit
38
+ * (`computeCoverDraw`) in the fixed variant, or the stretched container box
39
+ * (`w × h`) in classic. This keeps the clamp aligned with what's drawn even
40
+ * when the editor box aspect diverges from the image aspect.
41
+ *
42
+ * Fine tilt (`state.rotation`) inflates the frame to its axis-aligned bounding
43
+ * box (conservative). Flip is a no-op (mirrors about the centre).
44
+ *
45
+ * `maxScale` caps the cover floor: if covering the frame would need more zoom
46
+ * than the consumer allows, the floor is pinned to `maxScale` (so the returned
47
+ * `minScale ≤ maxScale` and the renderer's scale bounds never invert — at the
48
+ * cost of a residual gap, which is an unavoidable min/max-scale conflict).
49
+ */
50
+ export declare function clampCoverPanScale(state: TransformState, containerW: number, containerH: number, frame: {
51
+ x: number;
52
+ y: number;
53
+ width: number;
54
+ height: number;
55
+ }, drawW0: number, drawH0: number, maxScale: number): {
56
+ scale: number;
57
+ panX: number;
58
+ panY: number;
59
+ minScale: number;
60
+ };
61
+ /** Snap rotation near 0 within threshold. */
62
+ export declare function snapRotation(degrees: number, threshold?: number): number;
63
+ /** Constrain scale to range. */
64
+ export declare function constrainScale(scale: number, minScale: number, maxScale: number): number;
65
+ /** @deprecated Use clampCropToImage instead */
66
+ export declare const constrainCropBounds: typeof clampCropToImage;
67
+ /** @deprecated Use enforceAspectRatio instead */
68
+ export declare const constrainAspectRatio: typeof enforceAspectRatio;
@@ -0,0 +1,23 @@
1
+ import { TransformState, Size, Point } from '../core/types';
2
+ /** 2D affine transform matrix [a, b, c, d, e, f] (same layout as canvas). */
3
+ export type Matrix2D = [number, number, number, number, number, number];
4
+ export declare function identityMatrix(): Matrix2D;
5
+ export declare function multiplyMatrices(a: Matrix2D, b: Matrix2D): Matrix2D;
6
+ export declare function translateMatrix(tx: number, ty: number): Matrix2D;
7
+ export declare function scaleMatrix(sx: number, sy: number): Matrix2D;
8
+ export declare function rotateMatrix(degrees: number): Matrix2D;
9
+ /** @internal Build matrix as 6-element tuple. */
10
+ export declare function buildMatrix2D(canvasCx: number, canvasCy: number, rotation90: number, rotationFine: number, flipH: number, scale: number, panX: number, panY: number): Matrix2D;
11
+ /** Apply matrix to a point. */
12
+ export declare function transformPoint(m: Matrix2D, x: number, y: number): {
13
+ x: number;
14
+ y: number;
15
+ };
16
+ /** Invert a 2D affine matrix. */
17
+ export declare function invertMatrix(m: Matrix2D): Matrix2D;
18
+ /** Build the full image transform matrix. Spec section 8.2. */
19
+ export declare function buildTransformMatrix(state: TransformState, canvasSize: Size, _imageSize: Size): DOMMatrix;
20
+ /** Convert a point from image space to canvas space. Spec section 8.2. */
21
+ export declare function imageToCanvas(point: Point, matrix: DOMMatrix): Point;
22
+ /** Convert a point from canvas space to image space. Spec section 8.2. */
23
+ export declare function canvasToImage(point: Point, matrix: DOMMatrix): Point;
@@ -0,0 +1,12 @@
1
+ import { TransformState, CropRect, CropShapeName, HandlePosition } from '../core/types';
2
+ export declare function createInitialState(cropShape?: CropShapeName, imageWidth?: number, imageHeight?: number): TransformState;
3
+ export declare function applyRotateLeft(state: TransformState): TransformState;
4
+ export declare function applyFlipH(state: TransformState): TransformState;
5
+ export declare function applyFlipV(state: TransformState): TransformState;
6
+ export declare function applyRotation(state: TransformState, degrees: number): TransformState;
7
+ export declare function applyScale(state: TransformState, scale: number, minScale: number, maxScale: number): TransformState;
8
+ export declare function applyCropMove(state: TransformState, cropRect: CropRect): TransformState;
9
+ export declare function applyShapeChange(state: TransformState, shape: CropShapeName, imageWidth?: number, imageHeight?: number): TransformState;
10
+ export declare function applyPan(state: TransformState, dx: number, dy: number): TransformState;
11
+ /** Pure crop resize — spec section 8.1. dx/dy in normalized [0,1] space. */
12
+ export declare function applyCropResize(state: TransformState, handle: HandlePosition, dx: number, dy: number, cropShape?: CropShapeName, minCropSize?: number, imageWidth?: number, imageHeight?: number): TransformState;
@@ -0,0 +1,16 @@
1
+ type EventHandler = (...args: any[]) => void;
2
+ export declare class EventEmitter {
3
+ private listeners;
4
+ private onceMap;
5
+ on(event: string, handler: EventHandler): void;
6
+ off(event: string, handler: EventHandler): void;
7
+ emit(event: string, ...args: any[]): void;
8
+ once(event: string, handler: EventHandler): void;
9
+ removeAllListeners(): void;
10
+ }
11
+ export declare function addListener(el: EventTarget, event: string, handler: EventListenerOrEventListenerObject, options?: AddEventListenerOptions): () => void;
12
+ export type ThrottledFunction<T extends (...args: any[]) => void> = T & {
13
+ cancel(): void;
14
+ };
15
+ export declare function throttle<T extends (...args: any[]) => void>(fn: T, ms: number): ThrottledFunction<T>;
16
+ export {};
@@ -0,0 +1,12 @@
1
+ export declare function clamp(value: number, min: number, max: number): number;
2
+ export declare function lerp(a: number, b: number, t: number): number;
3
+ export declare function degreesToRadians(degrees: number): number;
4
+ export declare function radiansToDegrees(radians: number): number;
5
+ export declare function distance(x1: number, y1: number, x2: number, y2: number): number;
6
+ export declare function normalizeAngle(degrees: number): number;
7
+ /** Spring interpolation — returns new current value moving toward target. */
8
+ export declare function spring(current: number, target: number, velocity: {
9
+ v: number;
10
+ }, stiffness: number, damping: number, dt: number): number;
11
+ /** Check if a point is inside a rotated rectangle. */
12
+ export declare function pointInRotatedRect(px: number, py: number, cx: number, cy: number, w: number, h: number, angle: number): boolean;
package/package.json ADDED
@@ -0,0 +1,108 @@
1
+ {
2
+ "name": "@scaleflex/crop",
3
+ "version": "2.0.1",
4
+ "description": "Interactive image crop tool with rotation, flip, zoom and shape selection — Scaleflex Web Component",
5
+ "license": "MIT",
6
+ "author": "Scaleflex",
7
+ "type": "module",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ },
14
+ "./define": {
15
+ "types": "./dist/define.d.ts",
16
+ "import": "./dist/define.js",
17
+ "require": "./dist/define.cjs"
18
+ },
19
+ "./react": {
20
+ "types": "./dist/react/index.d.ts",
21
+ "import": "./dist/react/index.js",
22
+ "require": "./dist/react/index.cjs"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "!dist/**/*.map"
28
+ ],
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "sideEffects": [
33
+ "dist/define.js",
34
+ "dist/define.cjs",
35
+ "src/define.ts"
36
+ ],
37
+ "scripts": {
38
+ "dev": "vite --config config/vite.demo.config.ts",
39
+ "build": "npm run build:bundle && npm run build:react",
40
+ "build:bundle": "vite build --config config/vite.config.ts",
41
+ "build:react": "vite build --config config/vite.react.config.ts",
42
+ "build:demo": "vite build --config config/vite.demo.config.ts",
43
+ "build:cdn": "vite build --config config/vite.cdn.config.ts",
44
+ "release": "node scripts/release-cdn.mjs plugin",
45
+ "typecheck": "tsc --noEmit",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "test:coverage": "vitest run --coverage",
49
+ "lint": "eslint src/ tests/ --ext .ts,.tsx",
50
+ "prepublishOnly": "npm run build"
51
+ },
52
+ "dependencies": {
53
+ "lit": "^3.2.0"
54
+ },
55
+ "peerDependencies": {
56
+ "react": ">=18.0.0",
57
+ "react-dom": ">=18.0.0"
58
+ },
59
+ "peerDependenciesMeta": {
60
+ "react": {
61
+ "optional": true
62
+ },
63
+ "react-dom": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@testing-library/jest-dom": "^6.9.1",
69
+ "@testing-library/react": "^16.3.2",
70
+ "@types/node": "^25.2.3",
71
+ "@types/react": "^19.0.0",
72
+ "@types/react-dom": "^19.0.0",
73
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
74
+ "@typescript-eslint/parser": "^8.0.0",
75
+ "@vitejs/plugin-react": "^4.0.0",
76
+ "eslint": "^8.57.0",
77
+ "eslint-plugin-react-hooks": "^4.6.2",
78
+ "jsdom": "^25.0.0",
79
+ "react": "^19.0.0",
80
+ "react-dom": "^19.0.0",
81
+ "typescript": "^5.7.0",
82
+ "vite": "^6.0.0",
83
+ "vite-plugin-dts": "^4.0.0",
84
+ "vitest": "^3.0.0"
85
+ },
86
+ "keywords": [
87
+ "crop",
88
+ "image",
89
+ "editor",
90
+ "canvas",
91
+ "rotation",
92
+ "flip",
93
+ "zoom",
94
+ "cloudimage",
95
+ "scaleflex",
96
+ "web-component",
97
+ "lit",
98
+ "react"
99
+ ],
100
+ "repository": {
101
+ "type": "git",
102
+ "url": "https://github.com/scaleflex/image-crop.git"
103
+ },
104
+ "bugs": {
105
+ "url": "https://github.com/scaleflex/image-crop/issues"
106
+ },
107
+ "homepage": "https://scaleflex.github.io/image-crop/"
108
+ }