@wordpress/image-cropper 1.0.1-next.6deb34194.0
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/CHANGELOG.md +3 -0
- package/LICENSE.md +788 -0
- package/README.md +196 -0
- package/build/components/image-cropper/index.js +97 -0
- package/build/components/image-cropper/index.js.map +7 -0
- package/build/constants.js +37 -0
- package/build/constants.js.map +7 -0
- package/build/index.js +52 -0
- package/build/index.js.map +7 -0
- package/build/provider/index.js +86 -0
- package/build/provider/index.js.map +7 -0
- package/build/provider/use-image-cropper.js +189 -0
- package/build/provider/use-image-cropper.js.map +7 -0
- package/build/types.js +19 -0
- package/build/types.js.map +7 -0
- package/build/utils.js +107 -0
- package/build/utils.js.map +7 -0
- package/build-module/components/image-cropper/index.js +66 -0
- package/build-module/components/image-cropper/index.js.map +7 -0
- package/build-module/constants.js +10 -0
- package/build-module/constants.js.map +7 -0
- package/build-module/index.js +12 -0
- package/build-module/index.js.map +7 -0
- package/build-module/provider/index.js +50 -0
- package/build-module/provider/index.js.map +7 -0
- package/build-module/provider/use-image-cropper.js +167 -0
- package/build-module/provider/use-image-cropper.js.map +7 -0
- package/build-module/types.js +1 -0
- package/build-module/types.js.map +7 -0
- package/build-module/utils.js +78 -0
- package/build-module/utils.js.map +7 -0
- package/build-types/components/image-cropper/index.d.ts +3 -0
- package/build-types/components/image-cropper/index.d.ts.map +1 -0
- package/build-types/constants.d.ts +4 -0
- package/build-types/constants.d.ts.map +1 -0
- package/build-types/index.d.ts +5 -0
- package/build-types/index.d.ts.map +1 -0
- package/build-types/provider/index.d.ts +7 -0
- package/build-types/provider/index.d.ts.map +1 -0
- package/build-types/provider/use-image-cropper.d.ts +15 -0
- package/build-types/provider/use-image-cropper.d.ts.map +1 -0
- package/build-types/stories/index.story.d.ts +28 -0
- package/build-types/stories/index.story.d.ts.map +1 -0
- package/build-types/types.d.ts +42 -0
- package/build-types/types.d.ts.map +1 -0
- package/build-types/utils.d.ts +60 -0
- package/build-types/utils.d.ts.map +1 -0
- package/package.json +49 -0
- package/src/components/image-cropper/index.tsx +84 -0
- package/src/constants.ts +3 -0
- package/src/index.ts +4 -0
- package/src/provider/index.tsx +57 -0
- package/src/provider/use-image-cropper.ts +220 -0
- package/src/stories/index.story.tsx +351 -0
- package/src/stories/style.css +17 -0
- package/src/types.ts +54 -0
- package/src/utils.ts +146 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/image-cropper/src/provider/use-image-cropper.ts
|
|
21
|
+
var use_image_cropper_exports = {};
|
|
22
|
+
__export(use_image_cropper_exports, {
|
|
23
|
+
DEFAULT_INITIAL_STATE: () => DEFAULT_INITIAL_STATE,
|
|
24
|
+
default: () => useCropper
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(use_image_cropper_exports);
|
|
27
|
+
var import_dequal = require("dequal");
|
|
28
|
+
var import_element = require("@wordpress/element");
|
|
29
|
+
var import_constants = require("../constants");
|
|
30
|
+
var import_utils = require("../utils");
|
|
31
|
+
var DEFAULT_INITIAL_STATE = {
|
|
32
|
+
crop: {
|
|
33
|
+
x: 0,
|
|
34
|
+
y: 0,
|
|
35
|
+
width: 100,
|
|
36
|
+
height: 100
|
|
37
|
+
},
|
|
38
|
+
zoom: import_constants.MIN_ZOOM,
|
|
39
|
+
rotation: 0,
|
|
40
|
+
aspectRatio: 1,
|
|
41
|
+
flip: {
|
|
42
|
+
horizontal: false,
|
|
43
|
+
vertical: false
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
var DEFAULT_CROP_MEDIA_POSITION = {
|
|
47
|
+
x: 0,
|
|
48
|
+
y: 0
|
|
49
|
+
};
|
|
50
|
+
var DEFAULT_CROPPER_STATE = {
|
|
51
|
+
crop: DEFAULT_CROP_MEDIA_POSITION,
|
|
52
|
+
croppedArea: DEFAULT_INITIAL_STATE.crop,
|
|
53
|
+
croppedAreaPixels: null,
|
|
54
|
+
zoom: DEFAULT_INITIAL_STATE.zoom,
|
|
55
|
+
rotation: DEFAULT_INITIAL_STATE.rotation,
|
|
56
|
+
flip: DEFAULT_INITIAL_STATE.flip,
|
|
57
|
+
aspectRatio: DEFAULT_INITIAL_STATE.aspectRatio,
|
|
58
|
+
mediaSize: null
|
|
59
|
+
};
|
|
60
|
+
function useCropper() {
|
|
61
|
+
const [cropperState, setInternalCropperState] = (0, import_element.useState)(
|
|
62
|
+
DEFAULT_CROPPER_STATE
|
|
63
|
+
);
|
|
64
|
+
const [resetState, setInternalResetState] = (0, import_element.useState)(null);
|
|
65
|
+
const setCropperState = (0, import_element.useCallback)(
|
|
66
|
+
(newState) => {
|
|
67
|
+
setInternalCropperState((prev) => {
|
|
68
|
+
const updates = typeof newState === "function" ? newState(prev) : newState;
|
|
69
|
+
const normalizedUpdates = { ...updates };
|
|
70
|
+
if ("rotation" in normalizedUpdates && normalizedUpdates.rotation !== void 0) {
|
|
71
|
+
normalizedUpdates.rotation = (0, import_utils.normalizeRotation)(
|
|
72
|
+
normalizedUpdates.rotation
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return { ...prev, ...normalizedUpdates };
|
|
76
|
+
});
|
|
77
|
+
},
|
|
78
|
+
[]
|
|
79
|
+
);
|
|
80
|
+
const setResetState = (0, import_element.useCallback)(
|
|
81
|
+
(newResetState = null) => {
|
|
82
|
+
if (!newResetState) {
|
|
83
|
+
setInternalResetState(null);
|
|
84
|
+
setCropperState(DEFAULT_CROPPER_STATE);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (typeof newResetState === "object") {
|
|
88
|
+
const initialState = {
|
|
89
|
+
...DEFAULT_INITIAL_STATE,
|
|
90
|
+
...newResetState
|
|
91
|
+
};
|
|
92
|
+
setInternalResetState(initialState);
|
|
93
|
+
setCropperState(initialState);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[setCropperState, setInternalResetState]
|
|
97
|
+
);
|
|
98
|
+
const reset = (0, import_element.useCallback)(() => {
|
|
99
|
+
if (resetState) {
|
|
100
|
+
const resetUpdates = {
|
|
101
|
+
// Reset media position to center
|
|
102
|
+
crop: { x: 0, y: 0 },
|
|
103
|
+
// Reset cropped area pixels (will be recalculated)
|
|
104
|
+
croppedAreaPixels: null
|
|
105
|
+
};
|
|
106
|
+
if (resetState.crop) {
|
|
107
|
+
resetUpdates.croppedArea = resetState.crop;
|
|
108
|
+
}
|
|
109
|
+
if (resetState.zoom !== void 0) {
|
|
110
|
+
resetUpdates.zoom = resetState.zoom;
|
|
111
|
+
}
|
|
112
|
+
if (resetState.rotation !== void 0) {
|
|
113
|
+
resetUpdates.rotation = resetState.rotation;
|
|
114
|
+
}
|
|
115
|
+
if (resetState.aspectRatio !== void 0) {
|
|
116
|
+
resetUpdates.aspectRatio = resetState.aspectRatio;
|
|
117
|
+
}
|
|
118
|
+
if (resetState.flip !== void 0) {
|
|
119
|
+
resetUpdates.flip = resetState.flip;
|
|
120
|
+
}
|
|
121
|
+
setCropperState(resetUpdates);
|
|
122
|
+
} else {
|
|
123
|
+
setCropperState({ ...DEFAULT_CROPPER_STATE });
|
|
124
|
+
}
|
|
125
|
+
}, [resetState, setCropperState]);
|
|
126
|
+
const isDirty = (0, import_element.useMemo)(() => {
|
|
127
|
+
if (resetState) {
|
|
128
|
+
const currentState2 = {
|
|
129
|
+
crop: cropperState.croppedAreaPixels || cropperState.croppedArea,
|
|
130
|
+
zoom: cropperState.zoom,
|
|
131
|
+
rotation: (0, import_utils.normalizeRotation)(cropperState.rotation),
|
|
132
|
+
aspectRatio: cropperState.aspectRatio,
|
|
133
|
+
flip: cropperState.flip
|
|
134
|
+
};
|
|
135
|
+
return false === (0, import_dequal.dequal)(currentState2, resetState);
|
|
136
|
+
}
|
|
137
|
+
const currentState = {
|
|
138
|
+
crop: cropperState.croppedArea,
|
|
139
|
+
zoom: cropperState.zoom,
|
|
140
|
+
rotation: (0, import_utils.normalizeRotation)(cropperState.rotation),
|
|
141
|
+
aspectRatio: cropperState.aspectRatio,
|
|
142
|
+
flip: cropperState.flip
|
|
143
|
+
};
|
|
144
|
+
return false === (0, import_dequal.dequal)(currentState, DEFAULT_INITIAL_STATE);
|
|
145
|
+
}, [cropperState, resetState]);
|
|
146
|
+
const getCroppedImage = (0, import_element.useCallback)(
|
|
147
|
+
async (src) => {
|
|
148
|
+
if (!cropperState.croppedAreaPixels) {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
return (0, import_utils.getCroppedImage)(
|
|
152
|
+
src,
|
|
153
|
+
cropperState.croppedAreaPixels,
|
|
154
|
+
cropperState.rotation,
|
|
155
|
+
cropperState.flip
|
|
156
|
+
);
|
|
157
|
+
},
|
|
158
|
+
[
|
|
159
|
+
cropperState.croppedAreaPixels,
|
|
160
|
+
cropperState.rotation,
|
|
161
|
+
cropperState.flip
|
|
162
|
+
]
|
|
163
|
+
);
|
|
164
|
+
return (0, import_element.useMemo)(
|
|
165
|
+
() => ({
|
|
166
|
+
cropperState,
|
|
167
|
+
setCropperState,
|
|
168
|
+
resetState,
|
|
169
|
+
setResetState,
|
|
170
|
+
isDirty,
|
|
171
|
+
reset,
|
|
172
|
+
getCroppedImage
|
|
173
|
+
}),
|
|
174
|
+
[
|
|
175
|
+
cropperState,
|
|
176
|
+
setCropperState,
|
|
177
|
+
resetState,
|
|
178
|
+
setResetState,
|
|
179
|
+
isDirty,
|
|
180
|
+
reset,
|
|
181
|
+
getCroppedImage
|
|
182
|
+
]
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
186
|
+
0 && (module.exports = {
|
|
187
|
+
DEFAULT_INITIAL_STATE
|
|
188
|
+
});
|
|
189
|
+
//# sourceMappingURL=use-image-cropper.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/provider/use-image-cropper.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport { dequal } from 'dequal';\n\n/**\n * WordPress dependencies\n */\nimport { useMemo, useState, useCallback } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport type { Point, ImageCropperState, CropperState } from '../types';\nimport { MIN_ZOOM } from '../constants';\nimport {\n\tgetCroppedImage as getCroppedImageUtil,\n\tnormalizeRotation,\n} from '../utils';\n\nexport const DEFAULT_INITIAL_STATE: Required< ImageCropperState > = {\n\tcrop: {\n\t\tx: 0,\n\t\ty: 0,\n\t\twidth: 100,\n\t\theight: 100,\n\t},\n\tzoom: MIN_ZOOM,\n\trotation: 0,\n\taspectRatio: 1,\n\tflip: {\n\t\thorizontal: false,\n\t\tvertical: false,\n\t},\n};\n\nconst DEFAULT_CROP_MEDIA_POSITION: Point = {\n\tx: 0,\n\ty: 0,\n};\n\nconst DEFAULT_CROPPER_STATE: CropperState = {\n\tcrop: DEFAULT_CROP_MEDIA_POSITION,\n\tcroppedArea: DEFAULT_INITIAL_STATE.crop,\n\tcroppedAreaPixels: null,\n\tzoom: DEFAULT_INITIAL_STATE.zoom,\n\trotation: DEFAULT_INITIAL_STATE.rotation,\n\tflip: DEFAULT_INITIAL_STATE.flip,\n\taspectRatio: DEFAULT_INITIAL_STATE.aspectRatio,\n\tmediaSize: null,\n};\n\nexport default function useCropper() {\n\tconst [ cropperState, setInternalCropperState ] = useState< CropperState >(\n\t\tDEFAULT_CROPPER_STATE\n\t);\n\tconst [ resetState, setInternalResetState ] =\n\t\tuseState< ImageCropperState | null >( null );\n\n\t// Unified setter that supports both partial updates and function updates\n\tconst setCropperState = useCallback(\n\t\t(\n\t\t\tnewState:\n\t\t\t\t| Partial< CropperState >\n\t\t\t\t| ( ( prev: CropperState ) => Partial< CropperState > )\n\t\t) => {\n\t\t\tsetInternalCropperState( ( prev ) => {\n\t\t\t\tconst updates =\n\t\t\t\t\ttypeof newState === 'function'\n\t\t\t\t\t\t? newState( prev )\n\t\t\t\t\t\t: newState;\n\n\t\t\t\t// Apply normalization to rotation if it's being updated\n\t\t\t\tconst normalizedUpdates = { ...updates };\n\t\t\t\tif (\n\t\t\t\t\t'rotation' in normalizedUpdates &&\n\t\t\t\t\tnormalizedUpdates.rotation !== undefined\n\t\t\t\t) {\n\t\t\t\t\tnormalizedUpdates.rotation = normalizeRotation(\n\t\t\t\t\t\tnormalizedUpdates.rotation\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn { ...prev, ...normalizedUpdates };\n\t\t\t} );\n\t\t},\n\t\t[]\n\t);\n\n\tconst setResetState = useCallback(\n\t\t( newResetState: Partial< ImageCropperState > | null = null ) => {\n\t\t\t// If null, wipe the reset state and reset the cropper state to the default state.\n\t\t\tif ( ! newResetState ) {\n\t\t\t\tsetInternalResetState( null );\n\t\t\t\tsetCropperState( DEFAULT_CROPPER_STATE );\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif ( typeof newResetState === 'object' ) {\n\t\t\t\tconst initialState = {\n\t\t\t\t\t...DEFAULT_INITIAL_STATE,\n\t\t\t\t\t...newResetState,\n\t\t\t\t};\n\t\t\t\tsetInternalResetState( initialState );\n\t\t\t\tsetCropperState( initialState );\n\t\t\t}\n\t\t},\n\t\t[ setCropperState, setInternalResetState ]\n\t);\n\n\t/*\n\t * Resets the cropper state.\n\t */\n\tconst reset = useCallback( () => {\n\t\tif ( resetState ) {\n\t\t\t// Convert ImageCropperState to CropperState updates\n\t\t\tconst resetUpdates: Partial< CropperState > = {\n\t\t\t\t// Reset media position to center\n\t\t\t\tcrop: { x: 0, y: 0 },\n\t\t\t\t// Reset cropped area pixels (will be recalculated)\n\t\t\t\tcroppedAreaPixels: null,\n\t\t\t};\n\n\t\t\t// Set the cropped area from resetState (this is the target crop area)\n\t\t\tif ( resetState.crop ) {\n\t\t\t\tresetUpdates.croppedArea = resetState.crop;\n\t\t\t}\n\t\t\tif ( resetState.zoom !== undefined ) {\n\t\t\t\tresetUpdates.zoom = resetState.zoom;\n\t\t\t}\n\t\t\tif ( resetState.rotation !== undefined ) {\n\t\t\t\tresetUpdates.rotation = resetState.rotation;\n\t\t\t}\n\t\t\tif ( resetState.aspectRatio !== undefined ) {\n\t\t\t\tresetUpdates.aspectRatio = resetState.aspectRatio;\n\t\t\t}\n\t\t\tif ( resetState.flip !== undefined ) {\n\t\t\t\tresetUpdates.flip = resetState.flip;\n\t\t\t}\n\n\t\t\tsetCropperState( resetUpdates );\n\t\t} else {\n\t\t\tsetCropperState( { ...DEFAULT_CROPPER_STATE } );\n\t\t}\n\t}, [ resetState, setCropperState ] );\n\n\t/*\n\t * Returns true if the cropper state is dirty.\n\t * Compare against reset state if available, otherwise against default state.\n\t */\n\tconst isDirty = useMemo( () => {\n\t\tif ( resetState ) {\n\t\t\t// Compare the relevant cropper properties against reset state\n\t\t\tconst currentState = {\n\t\t\t\tcrop:\n\t\t\t\t\tcropperState.croppedAreaPixels || cropperState.croppedArea,\n\t\t\t\tzoom: cropperState.zoom,\n\t\t\t\trotation: normalizeRotation( cropperState.rotation ),\n\t\t\t\taspectRatio: cropperState.aspectRatio,\n\t\t\t\tflip: cropperState.flip,\n\t\t\t};\n\t\t\treturn false === dequal( currentState, resetState );\n\t\t}\n\n\t\t// Compare against default state using percentage values\n\t\tconst currentState = {\n\t\t\tcrop: cropperState.croppedArea,\n\t\t\tzoom: cropperState.zoom,\n\t\t\trotation: normalizeRotation( cropperState.rotation ),\n\t\t\taspectRatio: cropperState.aspectRatio,\n\t\t\tflip: cropperState.flip,\n\t\t};\n\t\treturn false === dequal( currentState, DEFAULT_INITIAL_STATE );\n\t}, [ cropperState, resetState ] );\n\n\t/**\n\t * Returns the cropped image.\n\t *\n\t * @param {string} src - The source of the image to crop.\n\t * @return {Promise<string | null>} A promise that resolves to the cropped image.\n\t */\n\tconst getCroppedImage = useCallback(\n\t\tasync ( src: string ) => {\n\t\t\tif ( ! cropperState.croppedAreaPixels ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn getCroppedImageUtil(\n\t\t\t\tsrc,\n\t\t\t\tcropperState.croppedAreaPixels,\n\t\t\t\tcropperState.rotation,\n\t\t\t\tcropperState.flip\n\t\t\t);\n\t\t},\n\t\t[\n\t\t\tcropperState.croppedAreaPixels,\n\t\t\tcropperState.rotation,\n\t\t\tcropperState.flip,\n\t\t]\n\t);\n\n\treturn useMemo(\n\t\t() => ( {\n\t\t\tcropperState,\n\t\t\tsetCropperState,\n\t\t\tresetState,\n\t\t\tsetResetState,\n\t\t\tisDirty,\n\t\t\treset,\n\t\t\tgetCroppedImage,\n\t\t} ),\n\t\t[\n\t\t\tcropperState,\n\t\t\tsetCropperState,\n\t\t\tresetState,\n\t\t\tsetResetState,\n\t\t\tisDirty,\n\t\t\treset,\n\t\t\tgetCroppedImage,\n\t\t]\n\t);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["currentState", "getCroppedImageUtil"]
|
|
7
|
+
}
|
package/build/types.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __copyProps = (to, from, except, desc) => {
|
|
7
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
|
+
for (let key of __getOwnPropNames(from))
|
|
9
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
10
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
11
|
+
}
|
|
12
|
+
return to;
|
|
13
|
+
};
|
|
14
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
15
|
+
|
|
16
|
+
// packages/image-cropper/src/types.ts
|
|
17
|
+
var types_exports = {};
|
|
18
|
+
module.exports = __toCommonJS(types_exports);
|
|
19
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/types.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport type { Point, Area, MediaSize } from 'react-easy-crop';\n\n// Re-export types for convenience.\nexport type { Point, Area, MediaSize };\n\nexport type Flip = {\n\thorizontal: boolean;\n\tvertical: boolean;\n};\n\nexport interface ImageCropperState {\n\trotation?: number;\n\tcrop?: Area;\n\tzoom?: number;\n\tflip?: Flip;\n\taspectRatio?: number;\n}\n\nexport interface CropperState {\n\tcrop: Point;\n\tcroppedArea?: Area;\n\tcroppedAreaPixels?: Area | null;\n\tzoom: number;\n\trotation: number;\n\tflip: Flip;\n\taspectRatio: number;\n\tmediaSize: MediaSize | null;\n}\n\nexport interface ImageCropperProps {\n\tsrc: string;\n\tonLoad?: ( mediaSize: MediaSize ) => void;\n\tminZoom?: number;\n\tmaxZoom?: number;\n}\n\nexport interface ImageCropperContextValue {\n\tcropperState: CropperState;\n\tsetCropperState: (\n\t\tnewState:\n\t\t\t| Partial< CropperState >\n\t\t\t| ( ( prev: CropperState ) => Partial< CropperState > )\n\t) => void;\n\tresetState: ImageCropperState | null;\n\tsetResetState: (\n\t\tnewResetState: Partial< ImageCropperState > | null\n\t) => void;\n\tisDirty: boolean;\n\treset: () => void;\n\tgetCroppedImage: ( src: string ) => Promise< string | null >;\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/build/utils.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/image-cropper/src/utils.ts
|
|
21
|
+
var utils_exports = {};
|
|
22
|
+
__export(utils_exports, {
|
|
23
|
+
createImage: () => createImage,
|
|
24
|
+
getCroppedImage: () => getCroppedImage,
|
|
25
|
+
getRadianAngle: () => getRadianAngle,
|
|
26
|
+
normalizeRotation: () => normalizeRotation,
|
|
27
|
+
rotateSize: () => rotateSize
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(utils_exports);
|
|
30
|
+
var normalizeRotation = (rotation) => {
|
|
31
|
+
if (rotation >= 0) {
|
|
32
|
+
return rotation % 360;
|
|
33
|
+
}
|
|
34
|
+
return (360 + rotation % 360) % 360;
|
|
35
|
+
};
|
|
36
|
+
var createImage = (url) => new Promise((resolve, reject) => {
|
|
37
|
+
const image = new Image();
|
|
38
|
+
image.addEventListener("load", () => resolve(image));
|
|
39
|
+
image.addEventListener("error", (error) => reject(error));
|
|
40
|
+
image.setAttribute("crossOrigin", "anonymous");
|
|
41
|
+
image.src = url;
|
|
42
|
+
});
|
|
43
|
+
function getRadianAngle(degreeValue) {
|
|
44
|
+
return degreeValue * Math.PI / 180;
|
|
45
|
+
}
|
|
46
|
+
function rotateSize(width, height, rotation) {
|
|
47
|
+
const rotRad = getRadianAngle(rotation);
|
|
48
|
+
return {
|
|
49
|
+
width: Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
|
|
50
|
+
height: Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
async function getCroppedImage(imageSrc, pixelCrop, rotation = 0, flip = { horizontal: false, vertical: false }) {
|
|
54
|
+
try {
|
|
55
|
+
const image = await createImage(imageSrc);
|
|
56
|
+
const canvas = document.createElement("canvas");
|
|
57
|
+
const ctx = canvas.getContext("2d");
|
|
58
|
+
if (!ctx) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const rotRad = getRadianAngle(rotation);
|
|
62
|
+
const { width: boundingBoxWidth, height: boundingBoxHeight } = rotateSize(image.width, image.height, rotation);
|
|
63
|
+
canvas.width = boundingBoxWidth;
|
|
64
|
+
canvas.height = boundingBoxHeight;
|
|
65
|
+
ctx.translate(boundingBoxWidth / 2, boundingBoxHeight / 2);
|
|
66
|
+
ctx.rotate(rotRad);
|
|
67
|
+
ctx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1);
|
|
68
|
+
ctx.translate(-image.width / 2, -image.height / 2);
|
|
69
|
+
ctx.drawImage(image, 0, 0);
|
|
70
|
+
const croppedCanvas = document.createElement("canvas");
|
|
71
|
+
const croppedCtx = croppedCanvas.getContext("2d");
|
|
72
|
+
if (!croppedCtx) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
croppedCanvas.width = pixelCrop.width;
|
|
76
|
+
croppedCanvas.height = pixelCrop.height;
|
|
77
|
+
croppedCtx.drawImage(
|
|
78
|
+
canvas,
|
|
79
|
+
pixelCrop.x,
|
|
80
|
+
pixelCrop.y,
|
|
81
|
+
pixelCrop.width,
|
|
82
|
+
pixelCrop.height,
|
|
83
|
+
0,
|
|
84
|
+
0,
|
|
85
|
+
pixelCrop.width,
|
|
86
|
+
pixelCrop.height
|
|
87
|
+
);
|
|
88
|
+
return new Promise((resolve) => {
|
|
89
|
+
croppedCanvas.toBlob((file) => {
|
|
90
|
+
if (file) {
|
|
91
|
+
resolve(URL.createObjectURL(file));
|
|
92
|
+
}
|
|
93
|
+
}, "image/jpeg");
|
|
94
|
+
});
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
100
|
+
0 && (module.exports = {
|
|
101
|
+
createImage,
|
|
102
|
+
getCroppedImage,
|
|
103
|
+
getRadianAngle,
|
|
104
|
+
normalizeRotation,
|
|
105
|
+
rotateSize
|
|
106
|
+
});
|
|
107
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/utils.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Normalizes a rotation value to be within the range of 0-360 degrees.\n * So a rotation of -90 becomes 270, -45 becomes 315, etc.\n * This is required because the `/media/edit` calculates the rotation value\n * by subtracting the rotation value from 0. ImageMagick rotates clockwise only and will subtract the rotation value from 360.\n * It's utterly confusing, and probably in need of a refactor.\n *\n * @param {number} rotation - The rotation value to normalize.\n * @return {number} The normalized rotation value.\n */\nexport const normalizeRotation = ( rotation: number ) => {\n\tif ( rotation >= 0 ) {\n\t\treturn rotation % 360;\n\t}\n\n\t// For negative rotations, convert to positive clockwise equivalent\n\treturn ( 360 + ( rotation % 360 ) ) % 360;\n};\n\n/**\n * Creates an image from a URL.\n *\n * @param {string} url - The URL of the image to create.\n * @return {Promise<HTMLImageElement>} A promise that resolves to the image.\n */\nexport const createImage = ( url: string ) =>\n\tnew Promise( ( resolve, reject ) => {\n\t\tconst image = new Image();\n\t\timage.addEventListener( 'load', () => resolve( image ) );\n\t\timage.addEventListener( 'error', ( error ) => reject( error ) );\n\t\timage.setAttribute( 'crossOrigin', 'anonymous' );\n\t\timage.src = url;\n\t} );\n\n/**\n * Converts a degree value to a radian value.\n *\n * @param {number} degreeValue - The degree value to convert.\n * @return {number} The radian value.\n */\nexport function getRadianAngle( degreeValue: number ) {\n\treturn ( degreeValue * Math.PI ) / 180;\n}\n\n/**\n * Returns the new bounding area of a rotated rectangle.\n *\n * @param {number} width - The width of the rectangle.\n * @param {number} height - The height of the rectangle.\n * @param {number} rotation - The rotation of the rectangle.\n * @return {Object} The new bounding area of the rotated rectangle.\n */\nexport function rotateSize( width: number, height: number, rotation: number ) {\n\tconst rotRad = getRadianAngle( rotation );\n\n\treturn {\n\t\twidth:\n\t\t\tMath.abs( Math.cos( rotRad ) * width ) +\n\t\t\tMath.abs( Math.sin( rotRad ) * height ),\n\t\theight:\n\t\t\tMath.abs( Math.sin( rotRad ) * width ) +\n\t\t\tMath.abs( Math.cos( rotRad ) * height ),\n\t};\n}\n\n/**\n * Crops an image to a given area.\n *\n * @param {string} imageSrc - The source of the image to crop.\n * @param {Object} pixelCrop - The area to crop.\n * @param pixelCrop.x\n * @param pixelCrop.y\n * @param pixelCrop.width\n * @param pixelCrop.height\n * @param {number} rotation - The rotation of the image.\n * @param {Object} flip - The flip of the image.\n * @return {Promise<string | null>} A promise that resolves to the cropped image.\n */\nexport async function getCroppedImage(\n\timageSrc: string,\n\tpixelCrop: { x: number; y: number; width: number; height: number },\n\trotation = 0,\n\tflip = { horizontal: false, vertical: false }\n): Promise< string | null > {\n\ttry {\n\t\tconst image = ( await createImage( imageSrc ) ) as HTMLImageElement;\n\t\tconst canvas = document.createElement( 'canvas' );\n\t\tconst ctx = canvas.getContext( '2d' );\n\n\t\tif ( ! ctx ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Calculate the rotation angle in radians.\n\t\tconst rotRad = getRadianAngle( rotation );\n\n\t\tconst { width: boundingBoxWidth, height: boundingBoxHeight } =\n\t\t\trotateSize( image.width, image.height, rotation );\n\n\t\t// Set canvas size to match the bounding box.\n\t\tcanvas.width = boundingBoxWidth;\n\t\tcanvas.height = boundingBoxHeight;\n\n\t\t// Translate and draw canvas context to a central location to allow rotating and flipping around the center.\n\t\tctx.translate( boundingBoxWidth / 2, boundingBoxHeight / 2 );\n\t\tctx.rotate( rotRad );\n\t\tctx.scale( flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1 );\n\t\tctx.translate( -image.width / 2, -image.height / 2 );\n\t\tctx.drawImage( image, 0, 0 );\n\n\t\tconst croppedCanvas = document.createElement( 'canvas' );\n\t\tconst croppedCtx = croppedCanvas.getContext( '2d' );\n\n\t\tif ( ! croppedCtx ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Set the size of the cropped canvas.\n\t\tcroppedCanvas.width = pixelCrop.width;\n\t\tcroppedCanvas.height = pixelCrop.height;\n\n\t\t// Draw the cropped image onto the new canvas.\n\t\tcroppedCtx.drawImage(\n\t\t\tcanvas,\n\t\t\tpixelCrop.x,\n\t\t\tpixelCrop.y,\n\t\t\tpixelCrop.width,\n\t\t\tpixelCrop.height,\n\t\t\t0,\n\t\t\t0,\n\t\t\tpixelCrop.width,\n\t\t\tpixelCrop.height\n\t\t);\n\n\t\t// Return as a blob.\n\t\treturn new Promise( ( resolve ) => {\n\t\t\tcroppedCanvas.toBlob( ( file ) => {\n\t\t\t\tif ( file ) {\n\t\t\t\t\tresolve( URL.createObjectURL( file ) );\n\t\t\t\t}\n\t\t\t}, 'image/jpeg' );\n\t\t} );\n\t} catch {\n\t\treturn null;\n\t}\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUO,IAAM,oBAAoB,CAAE,aAAsB;AACxD,MAAK,YAAY,GAAI;AACpB,WAAO,WAAW;AAAA,EACnB;AAGA,UAAS,MAAQ,WAAW,OAAU;AACvC;AAQO,IAAM,cAAc,CAAE,QAC5B,IAAI,QAAS,CAAE,SAAS,WAAY;AACnC,QAAM,QAAQ,IAAI,MAAM;AACxB,QAAM,iBAAkB,QAAQ,MAAM,QAAS,KAAM,CAAE;AACvD,QAAM,iBAAkB,SAAS,CAAE,UAAW,OAAQ,KAAM,CAAE;AAC9D,QAAM,aAAc,eAAe,WAAY;AAC/C,QAAM,MAAM;AACb,CAAE;AAQI,SAAS,eAAgB,aAAsB;AACrD,SAAS,cAAc,KAAK,KAAO;AACpC;AAUO,SAAS,WAAY,OAAe,QAAgB,UAAmB;AAC7E,QAAM,SAAS,eAAgB,QAAS;AAExC,SAAO;AAAA,IACN,OACC,KAAK,IAAK,KAAK,IAAK,MAAO,IAAI,KAAM,IACrC,KAAK,IAAK,KAAK,IAAK,MAAO,IAAI,MAAO;AAAA,IACvC,QACC,KAAK,IAAK,KAAK,IAAK,MAAO,IAAI,KAAM,IACrC,KAAK,IAAK,KAAK,IAAK,MAAO,IAAI,MAAO;AAAA,EACxC;AACD;AAeA,eAAsB,gBACrB,UACA,WACA,WAAW,GACX,OAAO,EAAE,YAAY,OAAO,UAAU,MAAM,GACjB;AAC3B,MAAI;AACH,UAAM,QAAU,MAAM,YAAa,QAAS;AAC5C,UAAM,SAAS,SAAS,cAAe,QAAS;AAChD,UAAM,MAAM,OAAO,WAAY,IAAK;AAEpC,QAAK,CAAE,KAAM;AACZ,aAAO;AAAA,IACR;AAGA,UAAM,SAAS,eAAgB,QAAS;AAExC,UAAM,EAAE,OAAO,kBAAkB,QAAQ,kBAAkB,IAC1D,WAAY,MAAM,OAAO,MAAM,QAAQ,QAAS;AAGjD,WAAO,QAAQ;AACf,WAAO,SAAS;AAGhB,QAAI,UAAW,mBAAmB,GAAG,oBAAoB,CAAE;AAC3D,QAAI,OAAQ,MAAO;AACnB,QAAI,MAAO,KAAK,aAAa,KAAK,GAAG,KAAK,WAAW,KAAK,CAAE;AAC5D,QAAI,UAAW,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAM,SAAS,CAAE;AACnD,QAAI,UAAW,OAAO,GAAG,CAAE;AAE3B,UAAM,gBAAgB,SAAS,cAAe,QAAS;AACvD,UAAM,aAAa,cAAc,WAAY,IAAK;AAElD,QAAK,CAAE,YAAa;AACnB,aAAO;AAAA,IACR;AAGA,kBAAc,QAAQ,UAAU;AAChC,kBAAc,SAAS,UAAU;AAGjC,eAAW;AAAA,MACV;AAAA,MACA,UAAU;AAAA,MACV,UAAU;AAAA,MACV,UAAU;AAAA,MACV,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,UAAU;AAAA,MACV,UAAU;AAAA,IACX;AAGA,WAAO,IAAI,QAAS,CAAE,YAAa;AAClC,oBAAc,OAAQ,CAAE,SAAU;AACjC,YAAK,MAAO;AACX,kBAAS,IAAI,gBAAiB,IAAK,CAAE;AAAA,QACtC;AAAA,MACD,GAAG,YAAa;AAAA,IACjB,CAAE;AAAA,EACH,QAAQ;AACP,WAAO;AAAA,EACR;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// packages/image-cropper/src/components/image-cropper/index.tsx
|
|
2
|
+
import Cropper from "react-easy-crop";
|
|
3
|
+
import { useCallback } from "@wordpress/element";
|
|
4
|
+
import { useImageCropper } from "../../provider";
|
|
5
|
+
import { MIN_ZOOM, MAX_ZOOM } from "../../constants";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
function ImageCropper({
|
|
8
|
+
src,
|
|
9
|
+
onLoad,
|
|
10
|
+
minZoom = MIN_ZOOM,
|
|
11
|
+
maxZoom = MAX_ZOOM,
|
|
12
|
+
...props
|
|
13
|
+
}) {
|
|
14
|
+
const { cropperState, setCropperState } = useImageCropper();
|
|
15
|
+
const { crop, zoom, rotation, aspectRatio, flip } = cropperState;
|
|
16
|
+
const setCrop = (newCrop) => setCropperState({ crop: newCrop });
|
|
17
|
+
const setZoom = (newZoom) => setCropperState({ zoom: newZoom });
|
|
18
|
+
const setRotation = (newRotation) => setCropperState({ rotation: newRotation });
|
|
19
|
+
const setMediaSize = (newMediaSize) => setCropperState({ mediaSize: newMediaSize });
|
|
20
|
+
const onCropComplete = useCallback(
|
|
21
|
+
(areaPercentage, areaPixels) => {
|
|
22
|
+
setCropperState({
|
|
23
|
+
croppedArea: areaPercentage,
|
|
24
|
+
croppedAreaPixels: areaPixels
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
[setCropperState]
|
|
28
|
+
);
|
|
29
|
+
return /* @__PURE__ */ jsx(
|
|
30
|
+
Cropper,
|
|
31
|
+
{
|
|
32
|
+
classes: {
|
|
33
|
+
containerClassName: "image-cropper__container",
|
|
34
|
+
cropAreaClassName: "image-cropper__crop-area",
|
|
35
|
+
mediaClassName: "image-cropper__image"
|
|
36
|
+
},
|
|
37
|
+
minZoom,
|
|
38
|
+
maxZoom,
|
|
39
|
+
rotation,
|
|
40
|
+
image: src,
|
|
41
|
+
setMediaSize,
|
|
42
|
+
crop,
|
|
43
|
+
zoom,
|
|
44
|
+
aspect: aspectRatio,
|
|
45
|
+
onCropChange: setCrop,
|
|
46
|
+
onZoomChange: setZoom,
|
|
47
|
+
onCropComplete,
|
|
48
|
+
onMediaLoaded: (loadedMediaSize) => {
|
|
49
|
+
onLoad?.(loadedMediaSize);
|
|
50
|
+
},
|
|
51
|
+
onRotationChange: setRotation,
|
|
52
|
+
transform: [
|
|
53
|
+
`translate(${crop.x}px, ${crop.y}px)`,
|
|
54
|
+
`rotateZ(${rotation}deg)`,
|
|
55
|
+
`rotateY(${flip.horizontal ? 180 : 0}deg)`,
|
|
56
|
+
`rotateX(${flip.vertical ? 180 : 0}deg)`,
|
|
57
|
+
`scale(${zoom})`
|
|
58
|
+
].join(" "),
|
|
59
|
+
...props
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
export {
|
|
64
|
+
ImageCropper as default
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/components/image-cropper/index.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport Cropper from 'react-easy-crop';\n\n/**\n * WordPress dependencies\n */\nimport { useCallback } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport { useImageCropper } from '../../provider';\nimport type { Area, ImageCropperProps, MediaSize, Point } from '../../types';\nimport { MIN_ZOOM, MAX_ZOOM } from '../../constants';\n\nexport default function ImageCropper( {\n\tsrc,\n\tonLoad,\n\tminZoom = MIN_ZOOM,\n\tmaxZoom = MAX_ZOOM,\n\t...props\n}: ImageCropperProps ) {\n\tconst { cropperState, setCropperState } = useImageCropper();\n\tconst { crop, zoom, rotation, aspectRatio, flip } = cropperState;\n\n\tconst setCrop = ( newCrop: Point ) => setCropperState( { crop: newCrop } );\n\tconst setZoom = ( newZoom: number ) => setCropperState( { zoom: newZoom } );\n\tconst setRotation = ( newRotation: number ) =>\n\t\tsetCropperState( { rotation: newRotation } );\n\tconst setMediaSize = ( newMediaSize: MediaSize ) =>\n\t\tsetCropperState( { mediaSize: newMediaSize } );\n\n\t/**\n\t * Handles the crop complete event, when a user stops interacting with the cropper.\n\t * Updates the cropped area in percentages and pixels.\n\t *\n\t * @param {Area} areaPercentage - The area percentage.\n\t * @param {Area} areaPixels - The area pixels.\n\t */\n\tconst onCropComplete = useCallback(\n\t\t( areaPercentage: Area, areaPixels: Area ) => {\n\t\t\tsetCropperState( {\n\t\t\t\tcroppedArea: areaPercentage,\n\t\t\t\tcroppedAreaPixels: areaPixels,\n\t\t\t} );\n\t\t},\n\t\t[ setCropperState ]\n\t);\n\n\treturn (\n\t\t<Cropper\n\t\t\tclasses={ {\n\t\t\t\tcontainerClassName: 'image-cropper__container',\n\t\t\t\tcropAreaClassName: 'image-cropper__crop-area',\n\t\t\t\tmediaClassName: 'image-cropper__image',\n\t\t\t} }\n\t\t\tminZoom={ minZoom }\n\t\t\tmaxZoom={ maxZoom }\n\t\t\trotation={ rotation }\n\t\t\timage={ src }\n\t\t\tsetMediaSize={ setMediaSize }\n\t\t\tcrop={ crop }\n\t\t\tzoom={ zoom }\n\t\t\taspect={ aspectRatio }\n\t\t\tonCropChange={ setCrop }\n\t\t\tonZoomChange={ setZoom }\n\t\t\tonCropComplete={ onCropComplete }\n\t\t\tonMediaLoaded={ ( loadedMediaSize: MediaSize ) => {\n\t\t\t\tonLoad?.( loadedMediaSize );\n\t\t\t} }\n\t\t\tonRotationChange={ setRotation }\n\t\t\ttransform={ [\n\t\t\t\t`translate(${ crop.x }px, ${ crop.y }px)`,\n\t\t\t\t`rotateZ(${ rotation }deg)`,\n\t\t\t\t`rotateY(${ flip.horizontal ? 180 : 0 }deg)`,\n\t\t\t\t`rotateX(${ flip.vertical ? 180 : 0 }deg)`,\n\t\t\t\t`scale(${ zoom })`,\n\t\t\t].join( ' ' ) }\n\t\t\t{ ...props }\n\t\t/>\n\t);\n}\n"],
|
|
5
|
+
"mappings": ";AAGA,OAAO,aAAa;AAKpB,SAAS,mBAAmB;AAK5B,SAAS,uBAAuB;AAEhC,SAAS,UAAU,gBAAgB;AAqCjC;AAnCa,SAAR,aAA+B;AAAA,EACrC;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV,GAAG;AACJ,GAAuB;AACtB,QAAM,EAAE,cAAc,gBAAgB,IAAI,gBAAgB;AAC1D,QAAM,EAAE,MAAM,MAAM,UAAU,aAAa,KAAK,IAAI;AAEpD,QAAM,UAAU,CAAE,YAAoB,gBAAiB,EAAE,MAAM,QAAQ,CAAE;AACzE,QAAM,UAAU,CAAE,YAAqB,gBAAiB,EAAE,MAAM,QAAQ,CAAE;AAC1E,QAAM,cAAc,CAAE,gBACrB,gBAAiB,EAAE,UAAU,YAAY,CAAE;AAC5C,QAAM,eAAe,CAAE,iBACtB,gBAAiB,EAAE,WAAW,aAAa,CAAE;AAS9C,QAAM,iBAAiB;AAAA,IACtB,CAAE,gBAAsB,eAAsB;AAC7C,sBAAiB;AAAA,QAChB,aAAa;AAAA,QACb,mBAAmB;AAAA,MACpB,CAAE;AAAA,IACH;AAAA,IACA,CAAE,eAAgB;AAAA,EACnB;AAEA,SACC;AAAA,IAAC;AAAA;AAAA,MACA,SAAU;AAAA,QACT,oBAAoB;AAAA,QACpB,mBAAmB;AAAA,QACnB,gBAAgB;AAAA,MACjB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAS;AAAA,MACT,cAAe;AAAA,MACf,cAAe;AAAA,MACf;AAAA,MACA,eAAgB,CAAE,oBAAgC;AACjD,iBAAU,eAAgB;AAAA,MAC3B;AAAA,MACA,kBAAmB;AAAA,MACnB,WAAY;AAAA,QACX,aAAc,KAAK,CAAE,OAAQ,KAAK,CAAE;AAAA,QACpC,WAAY,QAAS;AAAA,QACrB,WAAY,KAAK,aAAa,MAAM,CAAE;AAAA,QACtC,WAAY,KAAK,WAAW,MAAM,CAAE;AAAA,QACpC,SAAU,IAAK;AAAA,MAChB,EAAE,KAAM,GAAI;AAAA,MACV,GAAG;AAAA;AAAA,EACN;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/constants.ts"],
|
|
4
|
+
"sourcesContent": ["export const MIN_ZOOM = 1;\nexport const MAX_ZOOM = 5;\nexport const DEFAULT_ASPECT_RATIO_SLUG = 'original';\n"],
|
|
5
|
+
"mappings": ";AAAO,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,4BAA4B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// packages/image-cropper/src/index.ts
|
|
2
|
+
import { default as default2 } from "./components/image-cropper";
|
|
3
|
+
import { useImageCropper, default as default3 } from "./provider";
|
|
4
|
+
export * from "./types";
|
|
5
|
+
import { normalizeRotation } from "./utils";
|
|
6
|
+
export {
|
|
7
|
+
default2 as ImageCropper,
|
|
8
|
+
default3 as ImageCropperProvider,
|
|
9
|
+
normalizeRotation,
|
|
10
|
+
useImageCropper
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["export { default as ImageCropper } from './components/image-cropper';\nexport { useImageCropper, default as ImageCropperProvider } from './provider';\nexport * from './types';\nexport { normalizeRotation } from './utils';\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAoB,WAAXA,gBAA+B;AACxC,SAAS,iBAA4B,WAAXA,gBAAuC;AACjE,cAAc;AACd,SAAS,yBAAyB;",
|
|
6
|
+
"names": ["default"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// packages/image-cropper/src/provider/index.tsx
|
|
2
|
+
import { createContext, useContext, useMemo } from "@wordpress/element";
|
|
3
|
+
import useCropper from "./use-image-cropper";
|
|
4
|
+
import { MIN_ZOOM } from "../constants";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
var ImageCropperContext = createContext({
|
|
7
|
+
cropperState: {
|
|
8
|
+
crop: { x: 0, y: 0 },
|
|
9
|
+
croppedArea: { x: 0, y: 0, width: 100, height: 100 },
|
|
10
|
+
croppedAreaPixels: null,
|
|
11
|
+
zoom: MIN_ZOOM,
|
|
12
|
+
rotation: 0,
|
|
13
|
+
aspectRatio: 1,
|
|
14
|
+
flip: { horizontal: false, vertical: false },
|
|
15
|
+
mediaSize: null
|
|
16
|
+
},
|
|
17
|
+
setCropperState: () => {
|
|
18
|
+
},
|
|
19
|
+
resetState: null,
|
|
20
|
+
setResetState: () => {
|
|
21
|
+
},
|
|
22
|
+
isDirty: false,
|
|
23
|
+
reset: () => {
|
|
24
|
+
},
|
|
25
|
+
getCroppedImage: () => Promise.resolve(null)
|
|
26
|
+
});
|
|
27
|
+
function ImageCropperProvider({
|
|
28
|
+
children
|
|
29
|
+
}) {
|
|
30
|
+
const cropperApi = useCropper();
|
|
31
|
+
const contextValue = useMemo(() => {
|
|
32
|
+
return {
|
|
33
|
+
...cropperApi
|
|
34
|
+
};
|
|
35
|
+
}, [cropperApi]);
|
|
36
|
+
return /* @__PURE__ */ jsx(ImageCropperContext.Provider, { value: contextValue, children });
|
|
37
|
+
}
|
|
38
|
+
var useImageCropper = () => {
|
|
39
|
+
const context = useContext(ImageCropperContext);
|
|
40
|
+
if (!context) {
|
|
41
|
+
throw new Error("Missing ImageCropperContext");
|
|
42
|
+
}
|
|
43
|
+
return context;
|
|
44
|
+
};
|
|
45
|
+
export {
|
|
46
|
+
ImageCropperContext,
|
|
47
|
+
ImageCropperProvider as default,
|
|
48
|
+
useImageCropper
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/provider/index.tsx"],
|
|
4
|
+
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport { createContext, useContext, useMemo } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport useCropper from './use-image-cropper';\nimport type { ImageCropperContextValue } from '../types';\nimport { MIN_ZOOM } from '../constants';\n\nexport const ImageCropperContext = createContext< ImageCropperContextValue >( {\n\tcropperState: {\n\t\tcrop: { x: 0, y: 0 },\n\t\tcroppedArea: { x: 0, y: 0, width: 100, height: 100 },\n\t\tcroppedAreaPixels: null,\n\t\tzoom: MIN_ZOOM,\n\t\trotation: 0,\n\t\taspectRatio: 1,\n\t\tflip: { horizontal: false, vertical: false },\n\t\tmediaSize: null,\n\t},\n\tsetCropperState: () => {},\n\tresetState: null,\n\tsetResetState: () => {},\n\tisDirty: false,\n\treset: () => {},\n\tgetCroppedImage: () => Promise.resolve( null ),\n} );\n\nexport default function ImageCropperProvider( {\n\tchildren,\n}: {\n\tchildren: React.ReactNode;\n} ) {\n\tconst cropperApi = useCropper();\n\tconst contextValue = useMemo( () => {\n\t\treturn {\n\t\t\t...cropperApi,\n\t\t};\n\t}, [ cropperApi ] );\n\n\treturn (\n\t\t<ImageCropperContext.Provider value={ contextValue }>\n\t\t\t{ children }\n\t\t</ImageCropperContext.Provider>\n\t);\n}\n\nexport const useImageCropper = () => {\n\tconst context = useContext( ImageCropperContext );\n\tif ( ! context ) {\n\t\tthrow new Error( 'Missing ImageCropperContext' );\n\t}\n\treturn context;\n};\n"],
|
|
5
|
+
"mappings": ";AAGA,SAAS,eAAe,YAAY,eAAe;AAKnD,OAAO,gBAAgB;AAEvB,SAAS,gBAAgB;AAkCvB;AAhCK,IAAM,sBAAsB,cAA2C;AAAA,EAC7E,cAAc;AAAA,IACb,MAAM,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,IACnB,aAAa,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,KAAK,QAAQ,IAAI;AAAA,IACnD,mBAAmB;AAAA,IACnB,MAAM;AAAA,IACN,UAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAM,EAAE,YAAY,OAAO,UAAU,MAAM;AAAA,IAC3C,WAAW;AAAA,EACZ;AAAA,EACA,iBAAiB,MAAM;AAAA,EAAC;AAAA,EACxB,YAAY;AAAA,EACZ,eAAe,MAAM;AAAA,EAAC;AAAA,EACtB,SAAS;AAAA,EACT,OAAO,MAAM;AAAA,EAAC;AAAA,EACd,iBAAiB,MAAM,QAAQ,QAAS,IAAK;AAC9C,CAAE;AAEa,SAAR,qBAAuC;AAAA,EAC7C;AACD,GAEI;AACH,QAAM,aAAa,WAAW;AAC9B,QAAM,eAAe,QAAS,MAAM;AACnC,WAAO;AAAA,MACN,GAAG;AAAA,IACJ;AAAA,EACD,GAAG,CAAE,UAAW,CAAE;AAElB,SACC,oBAAC,oBAAoB,UAApB,EAA6B,OAAQ,cACnC,UACH;AAEF;AAEO,IAAM,kBAAkB,MAAM;AACpC,QAAM,UAAU,WAAY,mBAAoB;AAChD,MAAK,CAAE,SAAU;AAChB,UAAM,IAAI,MAAO,6BAA8B;AAAA,EAChD;AACA,SAAO;AACR;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|