@vivekjha659/react-image-editor 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vivek
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Readme.md ADDED
@@ -0,0 +1,138 @@
1
+ # @vivekjha659/react-image-editor
2
+
3
+ A portable, WhatsApp-style image editor modal for React and Next.js apps —
4
+ crop, freehand drawing, shapes (rectangle / circle / arrow / double-arrow /
5
+ curve), and text overlays. Ships with a dedicated mobile UI and a separate
6
+ desktop sidebar UI.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @vivekjha659/react-image-editor
12
+ ```
13
+
14
+ You'll also need these peer dependencies if your project doesn't already
15
+ have them:
16
+
17
+ ```bash
18
+ npm install react react-dom react-konva konva lucide-react react-icons react-toastify
19
+ ```
20
+
21
+ Import the stylesheet once, anywhere in your app (it's plain compiled CSS —
22
+ no Tailwind setup required on your end, even if you don't use Tailwind):
23
+
24
+ ```js
25
+ import "@vivekjha659/react-image-editor/style.css";
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ```jsx
31
+ import { useState } from "react";
32
+ import { ImageEditorModal } from "@vivekjha659/react-image-editor";
33
+
34
+ function Example() {
35
+ const [isOpen, setIsOpen] = useState(true);
36
+
37
+ return (
38
+ <ImageEditorModal
39
+ isOpen={isOpen}
40
+ onClose={() => setIsOpen(false)}
41
+ image={{ kind: "url", url: "https://example.com/photo.jpg", name: "photo.jpg" }}
42
+ onSave={(result) => {
43
+ // result = { blob, file, dataUrl, width, height }
44
+ console.log(result.file); // a File, ready to upload
45
+ }}
46
+ />
47
+ );
48
+ }
49
+ ```
50
+
51
+ ### Image input
52
+
53
+ `image` accepts any of these shapes:
54
+
55
+ ```ts
56
+ type EditableImageSource =
57
+ | { kind: "url"; url: string; name?: string }
58
+ | { kind: "file"; file: File; name?: string }
59
+ | { kind: "blob"; blob: Blob; name?: string }
60
+ | { kind: "dataUrl"; dataUrl: string; name?: string };
61
+ ```
62
+
63
+ Use whichever matches what you already have — a remote URL, a `File` from
64
+ an `<input type="file">`, a `Blob`, or a base64 data URL. No manual
65
+ conversion needed.
66
+
67
+ ### Save result
68
+
69
+ `onSave` receives an `ImageEditResult`:
70
+
71
+ ```ts
72
+ interface ImageEditResult {
73
+ blob: Blob;
74
+ file: File;
75
+ dataUrl: string;
76
+ width: number;
77
+ height: number;
78
+ }
79
+ ```
80
+
81
+ All four are derived from the same flattened canvas — pick whichever your
82
+ upload pipeline needs (`file`/`blob` for `FormData` uploads, `dataUrl` for
83
+ inline previews, `width`/`height` for validation or layout).
84
+
85
+ ## Next.js usage
86
+
87
+ This component reads `window`/`document` on mount (canvas, Konva, object
88
+ URLs) and **cannot be server-rendered**. In the Next.js App Router, every
89
+ component is a Server Component by default, so you must both:
90
+
91
+ 1. Import it with `next/dynamic` and `ssr: false`, and
92
+ 2. Only render it from inside a Client Component (a file with its own
93
+ `"use client"` at the top) — `ssr: false` is not itself valid inside a
94
+ Server Component.
95
+
96
+ ```jsx
97
+ // components/Editor.jsx
98
+ "use client";
99
+
100
+ import dynamic from "next/dynamic";
101
+
102
+ const ImageEditorModal = dynamic(
103
+ () =>
104
+ import("@vivekjha659/react-image-editor").then((m) => m.ImageEditorModal),
105
+ { ssr: false },
106
+ );
107
+
108
+ export default function Editor({ isOpen, onClose, image, onSave }) {
109
+ return (
110
+ <ImageEditorModal
111
+ isOpen={isOpen}
112
+ onClose={onClose}
113
+ image={image}
114
+ onSave={onSave}
115
+ />
116
+ );
117
+ }
118
+ ```
119
+
120
+ Then import `components/Editor.jsx` from wherever you need it — Server or
121
+ Client Components alike, since dynamic-loading is already handled inside
122
+ that wrapper.
123
+
124
+ ## API
125
+
126
+ | Export | Description |
127
+ |---|---|
128
+ | `ImageEditorModal` | The editor modal component. |
129
+ | `resolveImageSource(source)` | Normalizes an `EditableImageSource` into an `<img>`-usable `src`. Used internally; exported for advanced/custom flows. |
130
+ | `getImageSourceKey(source)` | Stable dependency-array key for an `EditableImageSource`. |
131
+ | `buildImageEditResult(canvas, options?)` | Builds an `ImageEditResult` from a canvas. Used internally by the save flow; exported for advanced/custom flows. |
132
+
133
+ Full type definitions ship in `dist/index.d.ts` — TypeScript/Next.js
134
+ consumers get autocomplete and inline docs out of the box.
135
+
136
+ ## License
137
+
138
+ MIT © Vivek
@@ -0,0 +1,99 @@
1
+ import * as React from "react";
2
+
3
+ /**
4
+ * The image to load into the editor. Accepts a remote/static URL, a File
5
+ * (e.g. from an <input type="file">), a Blob, or an already-encoded data
6
+ * URL. Object URLs created internally for "file"/"blob" sources are
7
+ * automatically revoked when the source changes or the modal unmounts.
8
+ */
9
+ export type EditableImageSource =
10
+ | { kind: "url"; url: string; name?: string }
11
+ | { kind: "file"; file: File; name?: string }
12
+ | { kind: "blob"; blob: Blob; name?: string }
13
+ | { kind: "dataUrl"; dataUrl: string; name?: string };
14
+
15
+ /**
16
+ * The result handed to `onSave` once the user confirms their edit. All four
17
+ * representations are derived from the same flattened canvas in a single
18
+ * pass — use whichever your upload pipeline needs.
19
+ */
20
+ export interface ImageEditResult {
21
+ blob: Blob;
22
+ file: File;
23
+ dataUrl: string;
24
+ width: number;
25
+ height: number;
26
+ }
27
+
28
+ export interface ImageEditorModalProps {
29
+ /** Controls whether the modal is rendered/visible. */
30
+ isOpen: boolean;
31
+ /** Called when the user cancels/dismisses the editor without saving. */
32
+ onClose: () => void;
33
+ /** The image to edit. See {@link EditableImageSource}. */
34
+ image: EditableImageSource;
35
+ /** Called with the edited image once the user confirms save/send. */
36
+ onSave: (result: ImageEditResult) => void;
37
+ }
38
+
39
+ /**
40
+ * A portable, WhatsApp-style image editor modal — crop, freehand drawing,
41
+ * shapes (rectangle / circle / arrow / double-arrow / curve), and text
42
+ * overlays, with a dedicated mobile UI and a desktop sidebar UI.
43
+ *
44
+ * This component reads `window`/`document` on mount and must not be
45
+ * server-rendered. In Next.js, import it with:
46
+ *
47
+ * ```ts
48
+ * const ImageEditorModal = dynamic(
49
+ * () => import("@yourscope/react-image-editor").then((m) => m.ImageEditorModal),
50
+ * { ssr: false },
51
+ * );
52
+ * ```
53
+ */
54
+ export declare function ImageEditorModal(
55
+ props: ImageEditorModalProps,
56
+ ): React.JSX.Element | null;
57
+
58
+ /**
59
+ * Normalizes any {@link EditableImageSource} into a usable <img>/canvas src.
60
+ * Returns a `cleanup()` you must call (e.g. from a `useEffect` return) to
61
+ * revoke any object URL created for "file"/"blob" sources. Called
62
+ * internally by `ImageEditorModal`; exported for advanced/custom usage.
63
+ */
64
+ export declare function resolveImageSource(
65
+ source: EditableImageSource | null | undefined,
66
+ ): {
67
+ src: string | null;
68
+ name: string | undefined;
69
+ isObjectUrl: boolean;
70
+ cleanup: () => void;
71
+ };
72
+
73
+ /**
74
+ * Produces a stable string key for an {@link EditableImageSource}, suitable
75
+ * for use as a `useEffect`/`useMemo` dependency array entry — avoids
76
+ * re-resolving (and re-revoking) object URLs just because a caller passed a
77
+ * fresh object literal with the same underlying value.
78
+ */
79
+ export declare function getImageSourceKey(
80
+ source: EditableImageSource | null | undefined,
81
+ ): string | null;
82
+
83
+ /**
84
+ * Builds an {@link ImageEditResult} from a canvas — blob, File, dataUrl, and
85
+ * dimensions, all derived from a single `canvas.toBlob()` call. Called
86
+ * internally by `ImageEditorModal`'s save flow; exported for advanced/
87
+ * custom usage.
88
+ */
89
+ export declare function buildImageEditResult(
90
+ canvas: HTMLCanvasElement,
91
+ options?: {
92
+ /** Defaults to "edited-image.png" */
93
+ fileName?: string;
94
+ /** Defaults to "image/png" */
95
+ mimeType?: string;
96
+ /** Only used for lossy mime types (e.g. "image/jpeg") */
97
+ quality?: number;
98
+ },
99
+ ): Promise<ImageEditResult>;