annotate-image 2.0.0-beta.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.
- package/LICENSE +338 -0
- package/dist/core.js +739 -0
- package/dist/core.min.js +1 -0
- package/dist/css/annotate.min.css +1 -0
- package/dist/jquery.js +746 -0
- package/dist/jquery.min.js +1 -0
- package/dist/react.js +794 -0
- package/dist/types/annotate-edit.d.ts +29 -0
- package/dist/types/annotate-image.d.ts +70 -0
- package/dist/types/annotate-view.d.ts +43 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/interactions.d.ts +7 -0
- package/dist/types/jquery.annotate.d.ts +6 -0
- package/dist/types/react.d.ts +43 -0
- package/dist/types/types.d.ts +119 -0
- package/dist/types/vue.d.ts +79 -0
- package/dist/vue.js +810 -0
- package/package.json +117 -0
- package/readme.md +389 -0
package/package.json
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "annotate-image",
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
|
+
"description": "Create Flickr-like comment annotations on images — draw rectangles, add notes, save via AJAX or static data",
|
|
5
|
+
"license": "GPL-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/flipbit/jquery-image-annotate.git"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"main": "dist/core.js",
|
|
16
|
+
"types": "dist/types/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/types/index.d.ts",
|
|
20
|
+
"import": "./dist/core.js",
|
|
21
|
+
"default": "./dist/core.min.js"
|
|
22
|
+
},
|
|
23
|
+
"./jquery": {
|
|
24
|
+
"types": "./dist/types/jquery.annotate.d.ts",
|
|
25
|
+
"import": "./dist/jquery.js",
|
|
26
|
+
"default": "./dist/jquery.min.js"
|
|
27
|
+
},
|
|
28
|
+
"./react": {
|
|
29
|
+
"types": "./dist/types/react.d.ts",
|
|
30
|
+
"import": "./dist/react.js"
|
|
31
|
+
},
|
|
32
|
+
"./vue": {
|
|
33
|
+
"types": "./dist/types/vue.d.ts",
|
|
34
|
+
"import": "./dist/vue.js"
|
|
35
|
+
},
|
|
36
|
+
"./css": "./dist/css/annotate.min.css"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"clean": "rm -rf dist",
|
|
40
|
+
"build:dirs": "mkdir -p dist/css dist/types",
|
|
41
|
+
"build:check": "tsc --noEmit",
|
|
42
|
+
"build:types": "tsc --emitDeclarationOnly --noEmit false --declaration --declarationDir dist/types --outDir dist/types",
|
|
43
|
+
"build:core": "esbuild src/index.ts --bundle --format=esm --outfile=dist/core.js",
|
|
44
|
+
"build:core:iife": "esbuild src/index.ts --bundle --minify --format=iife --global-name=AnnotateImage --outfile=dist/core.min.js",
|
|
45
|
+
"build:jquery": "esbuild src/jquery.annotate.ts --bundle --format=esm --external:jquery --outfile=dist/jquery.js",
|
|
46
|
+
"build:jquery:iife": "esbuild src/jquery.annotate.ts --bundle --minify --external:jquery --outfile=dist/jquery.min.js",
|
|
47
|
+
"build:react": "esbuild src/react.tsx --bundle --format=esm --external:react --external:react-dom --outfile=dist/react.js",
|
|
48
|
+
"build:vue": "esbuild src/vue.ts --bundle --format=esm --external:vue --outfile=dist/vue.js",
|
|
49
|
+
"build:css": "esbuild src/annotation.css --minify --outfile=dist/css/annotate.min.css",
|
|
50
|
+
"build": "npm run clean && npm run build:dirs && npm run build:check && npm run build:types && npm run build:core && npm run build:core:iife && npm run build:jquery && npm run build:jquery:iife && npm run build:react && npm run build:vue && npm run build:css",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"test:jquery4": "npm install jquery@4 --no-save && vitest run; STATUS=$?; npm install --no-save; exit $STATUS",
|
|
54
|
+
"test:e2e": "npx playwright test --config e2e/playwright.config.ts",
|
|
55
|
+
"lint": "eslint src/ test/",
|
|
56
|
+
"lint:fix": "eslint --fix src/ test/",
|
|
57
|
+
"format": "prettier --write 'src/**/*.{ts,css}' 'test/**/*.ts' '*.{js,ts,json}'",
|
|
58
|
+
"format:check": "prettier --check 'src/**/*.{ts,css}' 'test/**/*.ts' '*.{js,ts,json}'",
|
|
59
|
+
"demo": "npm run build && concurrently -k \"npm:demo:watch:*\" \"npm:demo:serve\"",
|
|
60
|
+
"demo:watch:core": "esbuild src/index.ts --bundle --format=esm --outfile=dist/core.js --watch",
|
|
61
|
+
"demo:watch:core-min": "esbuild src/index.ts --bundle --minify --format=iife --global-name=AnnotateImage --outfile=dist/core.min.js --watch",
|
|
62
|
+
"demo:watch:jquery": "esbuild src/jquery.annotate.ts --bundle --format=esm --external:jquery --outfile=dist/jquery.js --watch",
|
|
63
|
+
"demo:watch:jquery-min": "esbuild src/jquery.annotate.ts --bundle --minify --external:jquery --outfile=dist/jquery.min.js --watch",
|
|
64
|
+
"demo:watch:react": "esbuild src/react.tsx --bundle --format=esm --external:react --external:react-dom --outfile=dist/react.js --watch",
|
|
65
|
+
"demo:watch:vue": "esbuild src/vue.ts --bundle --format=esm --external:vue --outfile=dist/vue.js --watch",
|
|
66
|
+
"demo:watch:css": "esbuild src/annotation.css --minify --outfile=dist/css/annotate.min.css --watch",
|
|
67
|
+
"demo:serve": "live-server --port=8080 --open=demo/index.html --watch=dist,demo",
|
|
68
|
+
"demo:ci": "npm run build && concurrently -k \"npm:demo:watch:*\" \"npm:demo:serve:ci\"",
|
|
69
|
+
"demo:serve:ci": "live-server --port=8080 --no-browser --watch=dist,demo"
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@eslint/js": "^9.39.2",
|
|
73
|
+
"@playwright/test": "^1.58.2",
|
|
74
|
+
"@testing-library/dom": "^10.4.1",
|
|
75
|
+
"@testing-library/react": "^16.3.2",
|
|
76
|
+
"@types/jquery": "^3.5.33",
|
|
77
|
+
"@types/react": "^18.3.28",
|
|
78
|
+
"@types/react-dom": "^18.3.7",
|
|
79
|
+
"@vue/test-utils": "^2.4.6",
|
|
80
|
+
"concurrently": "^9.2.1",
|
|
81
|
+
"esbuild": "^0.27.3",
|
|
82
|
+
"eslint": "^9.39.2",
|
|
83
|
+
"eslint-config-prettier": "^10.1.8",
|
|
84
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
85
|
+
"eslint-plugin-vue": "^10.8.0",
|
|
86
|
+
"jquery": "^3.7.1",
|
|
87
|
+
"jsdom": "^28.0.0",
|
|
88
|
+
"live-server": "^1.2.2",
|
|
89
|
+
"prettier": "^3.8.1",
|
|
90
|
+
"react": "^18.3.1",
|
|
91
|
+
"react-dom": "^18.3.1",
|
|
92
|
+
"typescript": "^5.9.3",
|
|
93
|
+
"typescript-eslint": "^8.55.0",
|
|
94
|
+
"vitest": "^4.0.18",
|
|
95
|
+
"vue": "^3.5.28"
|
|
96
|
+
},
|
|
97
|
+
"peerDependencies": {
|
|
98
|
+
"jquery": ">=3.7.0",
|
|
99
|
+
"react": ">=18.0.0",
|
|
100
|
+
"react-dom": ">=18.0.0",
|
|
101
|
+
"vue": ">=3.3.0"
|
|
102
|
+
},
|
|
103
|
+
"peerDependenciesMeta": {
|
|
104
|
+
"jquery": {
|
|
105
|
+
"optional": true
|
|
106
|
+
},
|
|
107
|
+
"react": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"react-dom": {
|
|
111
|
+
"optional": true
|
|
112
|
+
},
|
|
113
|
+
"vue": {
|
|
114
|
+
"optional": true
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# Annotate Image
|
|
2
|
+
|
|
3
|
+
A JavaScript image annotation plugin that creates Flickr-like comment annotations on images. Users can draw rectangular regions on images, add text notes, and persist annotations via callbacks or AJAX.
|
|
4
|
+
|
|
5
|
+
Works standalone (vanilla JS), or with jQuery, React, or Vue. Framework adapters are tree-shakeable — only the one you import gets bundled.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install annotate-image
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
jQuery, React, and Vue are optional peer dependencies. Install only what you use:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
# For jQuery projects
|
|
17
|
+
npm install jquery
|
|
18
|
+
|
|
19
|
+
# For React projects
|
|
20
|
+
npm install react react-dom
|
|
21
|
+
|
|
22
|
+
# For Vue projects
|
|
23
|
+
npm install vue
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Vanilla JS
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<link rel="stylesheet" href="dist/css/annotate.min.css">
|
|
32
|
+
<script src="dist/core.min.js"></script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const instance = AnnotateImage.annotate(document.getElementById('myImage'), {
|
|
37
|
+
editable: true,
|
|
38
|
+
notes: [
|
|
39
|
+
{ top: 286, left: 161, width: 52, height: 37,
|
|
40
|
+
text: 'Small people on the steps',
|
|
41
|
+
id: 'note-1', editable: false },
|
|
42
|
+
{ top: 134, left: 179, width: 68, height: 74,
|
|
43
|
+
text: 'National Gallery Dome',
|
|
44
|
+
id: 'note-2', editable: true },
|
|
45
|
+
],
|
|
46
|
+
onChange(notes) { console.log('Notes changed:', notes); },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Programmatic control
|
|
50
|
+
instance.add(); // Enter add-note mode
|
|
51
|
+
instance.getNotes(); // Get current annotations
|
|
52
|
+
instance.clear(); // Remove all annotations
|
|
53
|
+
instance.destroy(); // Tear down completely
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### ES Modules
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import { annotate } from 'annotate-image';
|
|
60
|
+
import 'annotate-image/css';
|
|
61
|
+
|
|
62
|
+
const instance = annotate(document.getElementById('myImage'), {
|
|
63
|
+
editable: true,
|
|
64
|
+
notes: [],
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### jQuery
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<link rel="stylesheet" href="dist/css/annotate.min.css">
|
|
72
|
+
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
73
|
+
<script src="dist/jquery.min.js"></script>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
$(function() {
|
|
78
|
+
$('#myImage').annotateImage({
|
|
79
|
+
editable: true,
|
|
80
|
+
notes: [/* ... */],
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Destroy
|
|
85
|
+
$('#myImage').annotateImage('destroy');
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Or as an ES module:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import 'annotate-image/jquery';
|
|
92
|
+
import 'annotate-image/css';
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### React
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { useRef } from 'react';
|
|
99
|
+
import { AnnotateImage } from 'annotate-image/react';
|
|
100
|
+
import type { AnnotateImageRef } from 'annotate-image/react';
|
|
101
|
+
import 'annotate-image/css';
|
|
102
|
+
|
|
103
|
+
function App() {
|
|
104
|
+
const ref = useRef<AnnotateImageRef>(null);
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<>
|
|
108
|
+
<AnnotateImage
|
|
109
|
+
ref={ref}
|
|
110
|
+
src="/photo.jpg"
|
|
111
|
+
width={800}
|
|
112
|
+
height={600}
|
|
113
|
+
editable
|
|
114
|
+
notes={[]}
|
|
115
|
+
onChange={(notes) => console.log(notes)}
|
|
116
|
+
onSave={(note) => console.log('Saved:', note)}
|
|
117
|
+
onDelete={(note) => console.log('Deleted:', note)}
|
|
118
|
+
/>
|
|
119
|
+
<button onClick={() => ref.current?.add()}>Add Note</button>
|
|
120
|
+
<button onClick={() => console.log(ref.current?.getNotes())}>
|
|
121
|
+
Get Notes
|
|
122
|
+
</button>
|
|
123
|
+
</>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Vue
|
|
129
|
+
|
|
130
|
+
```vue
|
|
131
|
+
<script setup lang="ts">
|
|
132
|
+
import { ref } from 'vue';
|
|
133
|
+
import { AnnotateImage } from 'annotate-image/vue';
|
|
134
|
+
import 'annotate-image/css';
|
|
135
|
+
|
|
136
|
+
const annotator = ref();
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<template>
|
|
140
|
+
<AnnotateImage
|
|
141
|
+
ref="annotator"
|
|
142
|
+
src="/photo.jpg"
|
|
143
|
+
:width="800"
|
|
144
|
+
:height="600"
|
|
145
|
+
editable
|
|
146
|
+
:notes="[]"
|
|
147
|
+
@change="(notes) => console.log(notes)"
|
|
148
|
+
@save="(note) => console.log('Saved:', note)"
|
|
149
|
+
@delete="(note) => console.log('Deleted:', note)"
|
|
150
|
+
/>
|
|
151
|
+
<button @click="annotator?.add()">Add Note</button>
|
|
152
|
+
<button @click="console.log(annotator?.getNotes())">Get Notes</button>
|
|
153
|
+
</template>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## API Reference
|
|
157
|
+
|
|
158
|
+
### Core: `annotate(img, options?)`
|
|
159
|
+
|
|
160
|
+
Creates an annotation layer on an image element. Returns an `AnnotateImage` instance.
|
|
161
|
+
|
|
162
|
+
#### Options
|
|
163
|
+
|
|
164
|
+
| Option | Type | Default | Description |
|
|
165
|
+
|--------|------|---------|-------------|
|
|
166
|
+
| `editable` | `boolean` | `true` | Enable annotation editing |
|
|
167
|
+
| `notes` | `AnnotationNote[]` | `[]` | Initial annotations |
|
|
168
|
+
| `api` | `AnnotateApi` | — | Server persistence (see below) |
|
|
169
|
+
| `onChange` | `(notes: NoteData[]) => void` | — | Called after any notes mutation |
|
|
170
|
+
| `onSave` | `(note: NoteData) => void` | — | Called after a note is saved |
|
|
171
|
+
| `onDelete` | `(note: NoteData) => void` | — | Called after a note is deleted |
|
|
172
|
+
| `onLoad` | `(notes: NoteData[]) => void` | — | Called after notes are loaded |
|
|
173
|
+
| `onError` | `(ctx: AnnotateErrorContext) => void` | — | Called on API errors (defaults to `console.error`) |
|
|
174
|
+
|
|
175
|
+
#### `AnnotateApi`
|
|
176
|
+
|
|
177
|
+
Each field accepts a URL string (default fetch behavior) or a function for full control. Omit `api` entirely for static-only mode.
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
{
|
|
181
|
+
load?: string | (() => Promise<AnnotationNote[]>),
|
|
182
|
+
save?: string | ((note: NoteData) => Promise<SaveResult>),
|
|
183
|
+
delete?: string | ((note: NoteData) => Promise<void>),
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
#### Instance Methods
|
|
188
|
+
|
|
189
|
+
| Method | Returns | Description |
|
|
190
|
+
|--------|---------|-------------|
|
|
191
|
+
| `add()` | `boolean` | Enter add-note mode. Returns false if already editing. |
|
|
192
|
+
| `clear()` | `void` | Remove all annotations. |
|
|
193
|
+
| `destroy()` | `void` | Tear down completely. Idempotent. |
|
|
194
|
+
| `getNotes()` | `NoteData[]` | Current annotations (internal fields stripped). |
|
|
195
|
+
| `load()` | `void` | Rebuild views from the current notes array. |
|
|
196
|
+
|
|
197
|
+
#### Data Types
|
|
198
|
+
|
|
199
|
+
```typescript
|
|
200
|
+
// Full annotation (includes internal fields)
|
|
201
|
+
interface AnnotationNote {
|
|
202
|
+
id: string;
|
|
203
|
+
top: number;
|
|
204
|
+
left: number;
|
|
205
|
+
width: number;
|
|
206
|
+
height: number;
|
|
207
|
+
text: string;
|
|
208
|
+
editable: boolean;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Annotation data passed to callbacks (no internal fields)
|
|
212
|
+
type NoteData = Omit<AnnotationNote, 'view' | 'editable'>;
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### React: `<AnnotateImage>`
|
|
216
|
+
|
|
217
|
+
#### Props (`AnnotateImageProps`)
|
|
218
|
+
|
|
219
|
+
| Prop | Type | Default | Description |
|
|
220
|
+
|------|------|---------|-------------|
|
|
221
|
+
| `src` | `string` | — | Image URL (required) |
|
|
222
|
+
| `width` | `number` | — | Image width in pixels |
|
|
223
|
+
| `height` | `number` | — | Image height in pixels |
|
|
224
|
+
| `notes` | `AnnotationNote[]` | `[]` | Initial annotations |
|
|
225
|
+
| `editable` | `boolean` | `true` | Enable editing |
|
|
226
|
+
| `onChange` | `(notes: NoteData[]) => void` | — | Notes changed |
|
|
227
|
+
| `onSave` | `(note: NoteData) => void` | — | Note saved |
|
|
228
|
+
| `onDelete` | `(note: NoteData) => void` | — | Note deleted |
|
|
229
|
+
| `onLoad` | `(notes: NoteData[]) => void` | — | Notes loaded |
|
|
230
|
+
| `onError` | `(ctx: AnnotateErrorContext) => void` | — | Error occurred |
|
|
231
|
+
|
|
232
|
+
#### Ref Methods (`AnnotateImageRef`)
|
|
233
|
+
|
|
234
|
+
| Method | Returns | Description |
|
|
235
|
+
|--------|---------|-------------|
|
|
236
|
+
| `add()` | `void` | Enter add-note mode |
|
|
237
|
+
| `clear()` | `void` | Remove all annotations |
|
|
238
|
+
| `destroy()` | `void` | Tear down the instance |
|
|
239
|
+
| `getNotes()` | `NoteData[]` | Get current annotations |
|
|
240
|
+
|
|
241
|
+
### Vue: `<AnnotateImage>`
|
|
242
|
+
|
|
243
|
+
#### Props
|
|
244
|
+
|
|
245
|
+
| Prop | Type | Default | Description |
|
|
246
|
+
|------|------|---------|-------------|
|
|
247
|
+
| `src` | `String` | — | Image URL (required) |
|
|
248
|
+
| `width` | `Number` | — | Image width in pixels |
|
|
249
|
+
| `height` | `Number` | — | Image height in pixels |
|
|
250
|
+
| `notes` | `AnnotationNote[]` | — | Initial annotations |
|
|
251
|
+
| `editable` | `Boolean` | `true` | Enable editing |
|
|
252
|
+
|
|
253
|
+
#### Emits
|
|
254
|
+
|
|
255
|
+
| Event | Payload | Description |
|
|
256
|
+
|-------|---------|-------------|
|
|
257
|
+
| `change` | `NoteData[]` | Notes changed |
|
|
258
|
+
| `save` | `NoteData` | Note saved |
|
|
259
|
+
| `delete` | `NoteData` | Note deleted |
|
|
260
|
+
| `load` | `NoteData[]` | Notes loaded |
|
|
261
|
+
| `error` | `AnnotateErrorContext` | Error occurred |
|
|
262
|
+
|
|
263
|
+
#### Exposed Methods (via template ref)
|
|
264
|
+
|
|
265
|
+
| Method | Returns | Description |
|
|
266
|
+
|--------|---------|-------------|
|
|
267
|
+
| `add()` | `void` | Enter add-note mode |
|
|
268
|
+
| `clear()` | `void` | Remove all annotations |
|
|
269
|
+
| `destroy()` | `void` | Tear down the instance |
|
|
270
|
+
| `getNotes()` | `NoteData[]` | Get current annotations |
|
|
271
|
+
| `notes` | `Ref<NoteData[]>` | Reactive current notes |
|
|
272
|
+
|
|
273
|
+
## Tree Shaking
|
|
274
|
+
|
|
275
|
+
Each entry point (`annotate-image`, `annotate-image/jquery`, `annotate-image/react`, `annotate-image/vue`) is a separate bundle. Importing one does not pull in the others. Unused framework adapters are excluded automatically by bundlers that support package `exports`.
|
|
276
|
+
|
|
277
|
+
## Error Handling
|
|
278
|
+
|
|
279
|
+
The `onError` callback receives a context object:
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
interface AnnotateErrorContext {
|
|
283
|
+
type: 'load' | 'save' | 'delete';
|
|
284
|
+
error: Error;
|
|
285
|
+
note?: AnnotationNote;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
If no `onError` callback is provided, errors are logged to the console.
|
|
290
|
+
|
|
291
|
+
## Accessibility
|
|
292
|
+
|
|
293
|
+
The plugin supports keyboard navigation:
|
|
294
|
+
|
|
295
|
+
- **Tab** to navigate between annotation areas and controls
|
|
296
|
+
- **Enter/Space** to activate buttons and open editable annotations
|
|
297
|
+
- **Escape** to cancel editing
|
|
298
|
+
- Annotation areas and buttons have `role="button"` and `tabindex` attributes
|
|
299
|
+
- Focus is managed automatically when entering/exiting edit mode
|
|
300
|
+
- Visible focus styles on all interactive elements
|
|
301
|
+
|
|
302
|
+
## Demos
|
|
303
|
+
|
|
304
|
+
Run the demo server locally:
|
|
305
|
+
|
|
306
|
+
```sh
|
|
307
|
+
npm run demo
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
This opens a browser at `http://localhost:8080/demo/index.html` with links to demos including static annotations, AJAX loading, vanilla JS, React, Vue, and multiple instances.
|
|
311
|
+
|
|
312
|
+
## Build
|
|
313
|
+
|
|
314
|
+
```sh
|
|
315
|
+
npm install
|
|
316
|
+
npm run build # Type-check, bundle, minify
|
|
317
|
+
npm run build:check # Type-check only
|
|
318
|
+
npm test # Run tests
|
|
319
|
+
npm run test:jquery4 # Run tests against jQuery 4
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Build Output
|
|
323
|
+
|
|
324
|
+
`dist/` contains the built artifacts:
|
|
325
|
+
|
|
326
|
+
- `dist/core.js` — Core library (ESM, no dependencies)
|
|
327
|
+
- `dist/core.min.js` — Core library (IIFE, minified, `AnnotateImage` global)
|
|
328
|
+
- `dist/jquery.js` — jQuery adapter (ESM, jQuery external)
|
|
329
|
+
- `dist/jquery.min.js` — jQuery adapter (IIFE, minified, jQuery external)
|
|
330
|
+
- `dist/react.js` — React component (ESM, React external)
|
|
331
|
+
- `dist/vue.js` — Vue component (ESM, Vue external)
|
|
332
|
+
- `dist/css/annotate.min.css` — Minified styles
|
|
333
|
+
- `dist/types/` — TypeScript declaration files
|
|
334
|
+
|
|
335
|
+
## Browser Compatibility
|
|
336
|
+
|
|
337
|
+
All modern browsers (Chrome, Firefox, Safari, Edge). The plugin uses pointer events and `fetch`, so IE is not supported.
|
|
338
|
+
|
|
339
|
+
## History
|
|
340
|
+
|
|
341
|
+
### Version 2.0
|
|
342
|
+
|
|
343
|
+
* Package renamed from `jquery-image-annotate` to `annotate-image`
|
|
344
|
+
* Rewritten in TypeScript with vanilla DOM internals
|
|
345
|
+
* Removed jQuery UI dependency — drag/resize uses vanilla pointer events
|
|
346
|
+
* Added core vanilla JS API (`annotate()` factory) that works without jQuery
|
|
347
|
+
* jQuery adapter is now a thin wrapper registering `$.fn.annotateImage`
|
|
348
|
+
* Added React 18+ and Vue 3+ framework components
|
|
349
|
+
* Lifecycle callbacks: `onChange`, `onSave`, `onDelete`, `onLoad`
|
|
350
|
+
* `getNotes()` method for reading current annotations
|
|
351
|
+
* Replaced `$.ajax`/`$.getJSON` with `fetch`
|
|
352
|
+
* Build system replaced: Bower/Grunt removed in favor of npm/esbuild
|
|
353
|
+
* Added ESM and IIFE bundle outputs with TypeScript declarations
|
|
354
|
+
* Supports jQuery 3.x and 4.x
|
|
355
|
+
* Keyboard accessibility for all interactive elements
|
|
356
|
+
* Full test suite using Vitest
|
|
357
|
+
|
|
358
|
+
### Version 1.4 — 19th January, 2011
|
|
359
|
+
* Upgraded jQuery to version 1.7.1
|
|
360
|
+
|
|
361
|
+
### Version 1.3 — 22nd June, 2009
|
|
362
|
+
* Fixed a bug when creating a new annotation via AJAX.
|
|
363
|
+
* The Id of the annotation is expected to be returned as a JSON object from the response of the save call, e.g.
|
|
364
|
+
|
|
365
|
+
`{ "annotation_id": "000001" }`
|
|
366
|
+
|
|
367
|
+
### Version 1.2 — 24th April, 2009
|
|
368
|
+
* Fixed jQuery UI 1.3.2 compatibility.
|
|
369
|
+
* Forked source for jQuery 1.2.x and 1.3.x
|
|
370
|
+
* Notes now fade in/out.
|
|
371
|
+
* Tidied up CSS/positioning.
|
|
372
|
+
|
|
373
|
+
### Version 1.1 — 2nd April, 2009
|
|
374
|
+
* Fixed bug when annotating an image with no previous annotations.
|
|
375
|
+
|
|
376
|
+
### Version 1.0 — 11th March, 2009
|
|
377
|
+
* Initial release
|
|
378
|
+
|
|
379
|
+
## Credits
|
|
380
|
+
|
|
381
|
+
Based on the Drupal extension:
|
|
382
|
+
|
|
383
|
+
Image Annotations by Ronan Berder
|
|
384
|
+
hunvreus@gmail.com
|
|
385
|
+
http://drupal.org/project/image_annotate
|
|
386
|
+
|
|
387
|
+
## Licence
|
|
388
|
+
|
|
389
|
+
Released under the [GNU GPL v2](LICENSE) license.
|