@molecule/app-image-crop-cropperjs 1.0.0 → 1.0.2

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 (2) hide show
  1. package/README.md +185 -0
  2. package/package.json +8 -5
package/README.md ADDED
@@ -0,0 +1,185 @@
1
+ <!--
2
+ AUTO-GENERATED — DO NOT EDIT THIS FILE.
3
+ Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
4
+ Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
5
+ To change this document, edit the module-level JSDoc in src/index.ts.
6
+ Generated: 2026-08-04T01:51:05.744Z
7
+ -->
8
+
9
+ # @molecule/app-image-crop-cropperjs
10
+
11
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
+ > It is written to be read by coding agents as much as by people, and is generated from this
13
+ > package's source — edit `src/index.ts` JSDoc, not this file.
14
+
15
+ Cropper.js image-crop provider for `@molecule/app-image-crop` — a REAL
16
+ implementation backed by cropperjs v1. `createCropper({ src })` mounts a live
17
+ `Cropper` on an image element and every instance method delegates to the
18
+ corresponding cropperjs call, so `getCroppedCanvas()` returns the actual
19
+ cropped `<canvas>` (call `.toBlob()` / `.toDataURL()` on it to export) — not a
20
+ placeholder object.
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { provider } from '@molecule/app-image-crop-cropperjs'
26
+ import { setProvider, requireProvider } from '@molecule/app-image-crop'
27
+
28
+ setProvider(provider) // once, at app startup (bonds.ts)
29
+
30
+ const cropper = requireProvider().createCropper({ src: '/avatar.jpg', aspectRatio: 1 })
31
+ const canvas = cropper.getCroppedCanvas({ width: 200, height: 200 })
32
+ canvas.toBlob((blob) => uploadAvatar(blob), 'image/png')
33
+ ```
34
+
35
+ ## Type
36
+
37
+ `provider`
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install @molecule/app-image-crop-cropperjs @molecule/app-image-crop cropperjs
43
+ npm install -D @types/cropperjs
44
+ ```
45
+
46
+ ## API
47
+
48
+ ### Interfaces
49
+
50
+ #### `CropperjsConfig`
51
+
52
+ Provider-level defaults applied to every cropper created by the provider.
53
+
54
+ These map onto cropperjs constructor options. Per-cropper `CropperOptions`
55
+ (e.g. `guides`) take precedence over the matching field here.
56
+
57
+ ```typescript
58
+ interface CropperjsConfig {
59
+ /**
60
+ * Whether to show the dashed crop guide lines by default. Overridden per-cropper
61
+ * by `CropperOptions.guides`. Defaults to `true`.
62
+ */
63
+ guides?: boolean
64
+
65
+ /** Whether to render the checkerboard background behind the image. Defaults to `true`. */
66
+ background?: boolean
67
+
68
+ /**
69
+ * cropperjs view mode (0-3) constraining the crop box relative to the canvas /
70
+ * container. `1` restricts the crop box within the canvas. Defaults to `1`.
71
+ */
72
+ viewMode?: 0 | 1 | 2 | 3
73
+ }
74
+ ```
75
+
76
+ ### Functions
77
+
78
+ #### `createProvider(config)`
79
+
80
+ Creates a Cropper.js-based image crop provider.
81
+
82
+ ```typescript
83
+ function createProvider(config?: CropperjsConfig): ImageCropProvider
84
+ ```
85
+
86
+ - `config` — Optional provider-level defaults (`guides`, `background`, `viewMode`).
87
+
88
+ **Returns:** A configured `ImageCropProvider` backed by real cropperjs instances.
89
+
90
+ ### Constants
91
+
92
+ #### `provider`
93
+
94
+ Default Cropper.js provider instance.
95
+
96
+ ```typescript
97
+ const provider: ImageCropProvider
98
+ ```
99
+
100
+ ### Namespaces
101
+
102
+ #### `Cropper`
103
+
104
+ ## Core Interface
105
+
106
+ Implements `@molecule/app-image-crop` interface.
107
+
108
+ ## Bond Wiring
109
+
110
+ Setup function to register this provider with the core interface:
111
+
112
+ ```typescript
113
+ import { setProvider } from '@molecule/app-image-crop'
114
+ import { provider } from '@molecule/app-image-crop-cropperjs'
115
+
116
+ export function setupImageCropCropperjs(): void {
117
+ setProvider(provider)
118
+ }
119
+ ```
120
+
121
+ ## Injection Notes
122
+
123
+ ### Requirements
124
+
125
+ Peer dependencies:
126
+
127
+ - `@molecule/app-image-crop` ^1.0.1
128
+
129
+ ### Runtime Dependencies
130
+
131
+ - `@molecule/app-image-crop`
132
+ - `cropperjs`
133
+
134
+ - **Import cropperjs's stylesheet yourself** — this package does NOT:
135
+ `import 'cropperjs/dist/cropper.css'`. Without it the crop box, handles, and
136
+ drag guides render unstyled (an invisible/broken cropper), the same way Quill
137
+ needs its theme CSS.
138
+ - **Browser-only.** `createCropper()` calls `document.createElement('img')` and
139
+ `new Cropper(...)`; construct it in a client-only effect under SSR.
140
+ - **cropperjs initializes on the image's `load` event.** Reading `getCropData()`
141
+ or `getCroppedCanvas()` before the source has loaded returns empty/degenerate
142
+ data — drive them after load, or set the region explicitly with `setCropData()`
143
+ in natural-image coordinates first.
144
+ - **`circular` is a UI concern, not a pixel op.** cropperjs has no circular
145
+ pixel output; for a round avatar, style the crop box round in CSS
146
+ (`.cropper-view-box, .cropper-face { border-radius: 50% }`) and draw the
147
+ returned canvas into a rounded canvas before upload.
148
+ - **`OutputOptions.quality` applies at encode time**, not to canvas generation —
149
+ pass it to `canvas.toBlob(cb, 'image/jpeg', quality)` / `toDataURL(type, quality)`.
150
+ - Provider defaults (`guides`, `background`, `viewMode`) come from
151
+ `createProvider(config)`; per-cropper `CropperOptions.guides` wins over the
152
+ config default. `maxWidth`/`maxHeight` are enforced on output (cropperjs has no
153
+ max-crop-box constructor option).
154
+ - **Wire it** with `setProvider()` from `@molecule/app-image-crop` or
155
+ `bond('image-crop', provider)` from `@molecule/app-bond` — both route through
156
+ the shared registry; `requireProvider()` throws until one has run.
157
+
158
+ ## E2E Tests
159
+
160
+ Integration checklist — drive the real UI (live preview, no mocks), adapt each
161
+ item to this app's actual crop screen (avatar/photo upload, etc.), and check
162
+ every box off one by one. A box you can't check is an integration bug to fix —
163
+ not a skip. The cropper is headless, so verify the UI YOU rendered:
164
+
165
+ - [ ] Opening the crop screen loads the source image (`createCropper({ src })`)
166
+ and renders it with a visible crop-box overlay + drag handles drawn from
167
+ `getCropData()` — not a blank or full-bleed image with no selection.
168
+ - [ ] Dragging/resizing the crop box feeds the gesture into `setCropData()`,
169
+ `getCropData()` reflects the new x/y/width/height, and the on-page preview
170
+ (the `getCroppedCanvas()` output drawn into the page) updates to show ONLY
171
+ the selected area, not the whole image.
172
+ - [ ] With an aspect-ratio lock (e.g. `aspectRatio: 1` for an avatar) the crop
173
+ box stays that ratio while you resize — `getCropData()` width == height for
174
+ 1:1 — and `circular: true` clips the preview to a circle.
175
+ - [ ] `rotate()` / `zoom()` transform the source and the crop overlay follows:
176
+ `getCropData().rotate` / `scaleX` change and the preview re-renders the
177
+ transformed region — the selection isn't stranded on the old orientation.
178
+ - [ ] Applying the crop OUTPUTS the cropped image: `getCroppedCanvas()` pixels
179
+ match the selected region (not the full source), and downstream the SAVED
180
+ file is the cropped Blob (`canvas.toBlob` → upload) — re-fetch and render the
181
+ stored image and confirm it shows the crop, never the original.
182
+ - [ ] Min/max crop size is enforced — you cannot drag the box smaller than
183
+ `minWidth`/`minHeight` or larger than `maxWidth`/`maxHeight`.
184
+ - [ ] Cancel/close discards without mutating the source: the original image is
185
+ unchanged and no cropped result is saved.
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "@molecule/app-image-crop-cropperjs",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Cropper.js provider for @molecule/app-image-crop",
5
+ "homepage": "https://www.molecule.dev/packages/app-image-crop-cropperjs",
5
6
  "type": "module",
6
7
  "main": "dist/index.js",
7
8
  "types": "dist/index.d.ts",
@@ -17,7 +18,8 @@
17
18
  }
18
19
  },
19
20
  "files": [
20
- "dist"
21
+ "dist",
22
+ "README.md"
21
23
  ],
22
24
  "keywords": [
23
25
  "molecule",
@@ -28,17 +30,18 @@
28
30
  "license": "Apache-2.0",
29
31
  "repository": {
30
32
  "type": "git",
33
+ "url": "https://github.com/molecule-dev/molecule.git",
31
34
  "directory": "packages/app/bonds/image-crop/cropperjs"
32
35
  },
33
36
  "peerDependencies": {
34
- "@molecule/app-image-crop": "^1.0.0"
37
+ "@molecule/app-image-crop": "^1.0.1"
35
38
  },
36
39
  "devDependencies": {
37
- "@molecule/app-image-crop": "1.0.0",
40
+ "@molecule/app-image-crop": "1.0.2",
38
41
  "@types/cropperjs": "1.3.3",
39
42
  "@types/node": "26.1.2",
40
43
  "typescript": "6.0.3",
41
- "vitest": "4.1.10"
44
+ "vitest": "4.1.11"
42
45
  },
43
46
  "dependencies": {
44
47
  "cropperjs": "1.6.2"