mirador-annotation-editor-video 1.0.99 → 1.1.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 (28) hide show
  1. package/README.md +36 -35
  2. package/demo/src/index.js +8 -1
  3. package/es/IIIFUtils.js +3 -2
  4. package/es/TextEditor.js +7 -14
  5. package/es/annotationForm/AnnotationFormBody.js +1 -1
  6. package/es/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayTool.js +3 -2
  7. package/es/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayToolOptions.js +1 -1
  8. package/es/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Rectangle.js +1 -0
  9. package/es/annotationForm/AnnotationFormUtils.js +12 -18
  10. package/es/annotationForm/MultipleBodyTemplate.js +6 -5
  11. package/es/annotationForm/TargetSpatialInput.js +1 -1
  12. package/es/annotationForm/TextCommentInput.js +71 -0
  13. package/es/locales/locales_en.js +1 -0
  14. package/es/locales/locales_fr.js +1 -0
  15. package/package.json +2 -2
  16. package/src/IIIFUtils.js +7 -3
  17. package/src/TextEditor.js +7 -12
  18. package/src/annotationForm/AnnotationFormBody.js +1 -1
  19. package/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayTool.js +3 -1
  20. package/src/annotationForm/AnnotationFormOverlay/AnnotationFormOverlayToolOptions.js +1 -1
  21. package/src/annotationForm/AnnotationFormOverlay/KonvaDrawing/shapes/Rectangle.js +1 -0
  22. package/src/annotationForm/AnnotationFormUtils.js +9 -13
  23. package/src/annotationForm/MultiTagsInput.js +0 -5
  24. package/src/annotationForm/MultipleBodyTemplate.js +6 -5
  25. package/src/annotationForm/TargetSpatialInput.js +2 -2
  26. package/src/annotationForm/TextCommentInput.js +72 -0
  27. package/src/locales/locales_en.js +1 -0
  28. package/src/locales/locales_fr.js +1 -0
package/README.md CHANGED
@@ -4,57 +4,54 @@
4
4
 
5
5
  ### Generalities
6
6
 
7
- `mirador-annotation-editor-video`(also known as "MAEV") is a [Mirador 4](https://github.com/projectmirador/mirador) plugin that
7
+ `mirador-annotation-editor-video`(also known as "MAEV") is a [Mirador 4](https://github.com/projectmirador/mirador)
8
+ plugin that
8
9
  adds annotation creation tools to the user interface. It support both image and video annotation.
9
10
 
10
11
  ### Copyrights
11
12
 
13
+ Originally forked from https://github.com/ARVEST-APP/mirador-annotation-editor-video
14
+
12
15
  #### Licence
13
16
 
14
17
  This plugin is released under the **GPL v3** license unlike MAE and the original plugin.
15
18
 
16
- Please acknowledge that any modification you make must be distributed under a compatible licence and cannot be closed
19
+ Please acknowledge that any modification you make must be distributed under a compatible licence and cannot be closed
17
20
  source.
18
21
 
19
- If you need to integrate this code base in closed source pieces of software, please contact us, so we can discuss dual
20
- licencing.
21
-
22
- #### Property
23
-
24
- The base of this software (up to V1) is the property of [SATT Ouest Valorisation](https://www.ouest-valorisation.fr/)
25
- that funded its development under the French public contract AO-MA2023-0004-DV5189.
22
+ If you need to integrate this code base in closed source pieces of software, please contact us, so we can discuss dual
23
+ licencing.
26
24
 
27
- #### Authors
25
+ #### Authors
28
26
 
29
27
  The authors of this software are :
30
28
 
31
29
  - Clarisse Bardiot (concept and use cases)
32
30
  - Jacob Hart (specifications)
33
31
  - [Tétras Libre SARL](https://tetras-libre.fr) (development):
34
- - David Rouquet
35
- - Anthony Geourjon
36
- - Antoine Roy
32
+ - David Rouquet
33
+ - Anthony Geourjon
34
+ - Antoine Roy
37
35
 
38
36
  #### Contributors (updated february 2024)
39
37
 
40
- - AZOPSOFT SAS
41
- - Samuel Jugnet (especially code for the Konvas part)
42
- - Loïs Poujade (especially the original modifications to annotate videos)
38
+ - AZOPSOFT SAS
39
+ - Samuel Jugnet (especially code for the Konvas part)
43
40
 
44
- ### General functionalities
41
+ ### General functionalities
45
42
 
46
- - Activate a panel with tools to create annotations on IIIF documents (manifests) containing images **and videos with
47
- MAEV**
43
+ - Activate a panel with tools to create annotations on IIIF documents (manifests) containing images **and videos with
44
+ MAEV**
48
45
  - Spatial and temporal targets for annotations
49
46
  - Overlay annotations (geometric forms, free hand drawing, text and images)
50
47
  - Textual/semantic annotations and tags
51
48
  - Annotation metadata (based on Dublin Core)
52
49
  - Annotation with another manifest -> network of IIIF documents
53
50
 
54
- ### Technical aspects
51
+ ### Technical aspects
55
52
 
56
53
  - Update to Material UI 5 and React 18 to follow latest Mirador upgrades (We support mirador": "4.0.0-alpha.2",
57
- - The [paperjs](http://paperjs.org/ ) library has been replaced with [Konvas](https://konvajs.org)
54
+ - The [paperjs](http://paperjs.org/ ) library has been replaced with [Konvas](https://konvajs.org)
58
55
  - Major refactoring since the original `[mirador-annotations](https://github.com/ProjectMirador/mirador-annotations/)
59
56
  plugins`
60
57
  - Works with the original [Mirador 4](https://github.com/projectmirador/mirador) if you need only image annotation
@@ -70,18 +67,22 @@ npm install mirador-annotation-editor
70
67
  You can override existing annotation plugin with your own versions by using npm. We support React 18 and MUI 5.
71
68
 
72
69
  Update your `package.json` file to include the following dependencies and devDependencies:
70
+
73
71
  ```js
74
- "mirador-annotations": "npm:mirador-annotation-editor-video@^1.0.10",
72
+ "mirador-annotations"
73
+ :
74
+ "npm:mirador-annotation-editor-video@^1.0.10",
75
75
  ```
76
76
 
77
77
  You need also to use the custom version of Mirador 4.
78
78
 
79
79
  ```js
80
- "mirador" : "npm@mirador-video@^1.0.17",
80
+ "mirador"
81
+ :
82
+ "npm@mirador-video@^1.0.17",
81
83
  ```
82
84
 
83
- If you encounter this error :
84
-
85
+ If you encounter this error :
85
86
 
86
87
  ## Install (local)
87
88
 
@@ -101,23 +102,23 @@ npm start
101
102
  ```
102
103
 
103
104
  ## Use MAE with video annotation support
104
- - If you need video annotation, you can use
105
- [our fork of Mirador: mirador-video](https://github.com/SCENE-CE/mirador-video)
106
- - In addition, we have developed a wrapper of MAE to support video annotation. This wrapper is called **MAEV** and is
107
- available in the [mirador-annotation-editor-video](https://github.com/SCENE-CE/mirador-annotation-editor-video)
108
- repository.
109
105
 
106
+ - If you need video annotation, you can use
107
+ [our fork of Mirador: mirador-video](https://github.com/SCENE-CE/mirador-video)
108
+ - In addition, we have developed a wrapper of MAE to support video annotation. This wrapper is called **MAEV** and is
109
+ available in the [mirador-annotation-editor-video](https://github.com/SCENE-CE/mirador-annotation-editor-video)
110
+ repository.
110
111
 
111
112
  ## Persisting Annotations
112
- Persisting annotations requires implementing a IIIF annotation server. Several
113
+
114
+ Persisting annotations requires implementing a IIIF annotation server. Several
113
115
  [examples of annotation servers](https://github.com/IIIF/awesome-iiif#annotation-servers) are available on iiif-awesome.
114
116
 
115
- `mirador-annotation-editor` currently supports adapters for
116
- [annotot](https://github.com/ProjectMirador/mirador-annotations/blob/master/src/AnnototAdapter.js) and
117
- [local storage](https://github.com/ProjectMirador/mirador-annotations/blob/master/src/LocalStorageAdapter.js). We
117
+ `mirador-annotation-editor` currently supports adapters for
118
+ [annotot](https://github.com/ProjectMirador/mirador-annotations/blob/master/src/AnnototAdapter.js) and
119
+ [local storage](https://github.com/ProjectMirador/mirador-annotations/blob/master/src/LocalStorageAdapter.js). We
118
120
  welcome contributions of adapters for other annotation servers.
119
121
 
120
-
121
122
  ## Contribute
122
123
 
123
124
  Our plugin follow the Mirador guidelines. Development, design, and maintenance is driven by community needs and ongoing
package/demo/src/index.js CHANGED
@@ -6,7 +6,14 @@ import { manifestsCatalog } from './manifestsCatalog';
6
6
  const config = {
7
7
  annotation: {
8
8
  adapter: (canvasId) => new LocalStorageAdapter(`localStorage://?canvasId=${canvasId}`, 'Anonymous User'),
9
- commentTemplate: '<h4>Comment</h4><p>comment content</p>',
9
+ commentTemplates: [{
10
+ title: 'Template',
11
+ content: '<h4>Comment</h4><p>comment content</p>',
12
+ },
13
+ {
14
+ title: 'Template 2',
15
+ content: '<h4>Comment2</h4><p>comment content</p>',
16
+ }],
10
17
  exportLocalStorageAnnotations: false, // display annotation JSON export button
11
18
  tagsSuggestions: ['Mirador', 'Awesome', 'Viewer', 'IIIF'],
12
19
  },
package/es/IIIFUtils.js CHANGED
@@ -106,7 +106,7 @@ const getIIIFTargetFromMaeData = (maeData, canvasId, windowId = null, playerScal
106
106
  case _AnnotationFormUtils.TEMPLATE.MULTIPLE_BODY_TYPE:
107
107
  case _AnnotationFormUtils.TEMPLATE.TEXT_TYPE:
108
108
  // In some case the target can be simplified in a string
109
- if (maeTarget.drawingState.shapes.length === 1 && maeTarget.drawingState.shapes[0].type === _KonvaUtils.SHAPES_TOOL.RECTANGLE) {
109
+ if (isSimpleTarget(maeTarget.drawingState.shapes)) {
110
110
  return getIIIFTargetFromRectangleShape(maeTarget, canvasId, maeTarget.drawingState.shapes[0]);
111
111
  }
112
112
  // On the other case, the target is a SVG
@@ -119,6 +119,8 @@ const getIIIFTargetFromMaeData = (maeData, canvasId, windowId = null, playerScal
119
119
  // Default return
120
120
  return getIIIFTargetFullCanvas(maeData, canvasId);
121
121
  };
122
+ exports.getIIIFTargetFromMaeData = getIIIFTargetFromMaeData;
123
+ const isSimpleTarget = shapes => shapes.length === 1 && shapes[0].type === _KonvaUtils.SHAPES_TOOL.RECTANGLE && shapes[0].strokeColor === _AnnotationFormUtils.TARGET_TOOL_STATE.strokeColor && shapes[0].fillColor === _AnnotationFormUtils.TARGET_TOOL_STATE.fillColor;
122
124
 
123
125
  /**
124
126
  * Get the IIIF target from a Konva annotation (Drawing template)
@@ -126,7 +128,6 @@ const getIIIFTargetFromMaeData = (maeData, canvasId, windowId = null, playerScal
126
128
  * @param canvasId
127
129
  * @returns {`${string}#${string}`}
128
130
  */
129
- exports.getIIIFTargetFromMaeData = getIIIFTargetFromMaeData;
130
131
  const getIIIFTargetFromKonvaType = (maeData, canvasId) => {
131
132
  // Simplified target for Konva annotation
132
133
  console.log('Implement target as string with Konva annotation');
package/es/TextEditor.js CHANGED
@@ -4,14 +4,12 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
- var _react = _interopRequireWildcard(require("react"));
7
+ var _react = _interopRequireDefault(require("react"));
8
8
  var _propTypes = _interopRequireDefault(require("prop-types"));
9
9
  var _reactQuill = _interopRequireDefault(require("react-quill"));
10
10
  require("react-quill/dist/quill.snow.css");
11
11
  var _styles = require("@mui/material/styles");
12
12
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
- function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
14
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
15
13
  const StyledReactQuill = (0, _styles.styled)(_reactQuill.default)(({
16
14
  theme
17
15
  }) => ({
@@ -22,20 +20,15 @@ const StyledReactQuill = (0, _styles.styled)(_reactQuill.default)(({
22
20
 
23
21
  /** Rich text editor for annotation body */
24
22
  function TextEditor({
25
- annoHtml,
26
- updateAnnotationBody
23
+ text,
24
+ setText
27
25
  }) {
28
- const [editorHtml, setEditorHtml] = (0, _react.useState)(annoHtml);
29
-
30
26
  /**
31
27
  * Handle Change On ReactQuil Editor
32
28
  * @param html
33
29
  */
34
30
  const handleChange = html => {
35
- setEditorHtml(html);
36
- if (updateAnnotationBody) {
37
- updateAnnotationBody(html);
38
- }
31
+ setText(html);
39
32
  };
40
33
  const modules = {
41
34
  toolbar: [[{
@@ -60,7 +53,7 @@ function TextEditor({
60
53
  return /*#__PURE__*/_react.default.createElement("div", {
61
54
  "data-text-editor": "name"
62
55
  }, /*#__PURE__*/_react.default.createElement(StyledReactQuill, {
63
- value: editorHtml,
56
+ value: text,
64
57
  onChange: handleChange,
65
58
  placeholder: "Your text here",
66
59
  bounds: "[data-text-editor=\"name\"]",
@@ -69,7 +62,7 @@ function TextEditor({
69
62
  }));
70
63
  }
71
64
  TextEditor.propTypes = {
72
- annoHtml: _propTypes.default.string.isRequired,
73
- updateAnnotationBody: _propTypes.default.func.isRequired
65
+ setText: _propTypes.default.func.isRequired,
66
+ text: _propTypes.default.string.isRequired
74
67
  };
75
68
  var _default = exports.default = TextEditor;
@@ -88,7 +88,7 @@ function AnnotationFormBody({
88
88
  saveAnnotation: saveAnnotation,
89
89
  t: t,
90
90
  windowId: windowId,
91
- commentTemplate: config?.annotation?.commentTemplate ?? '',
91
+ commentTemplate: config?.annotation?.commentTemplates ?? [],
92
92
  tagsSuggestions: config?.annotation?.tagsSuggestions ?? []
93
93
  })), debugMode && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_Typography.default, null, playerReferences.getMediaType()), /*#__PURE__*/_react.default.createElement(_Typography.default, null, t('scale'), ":", playerReferences.getScale()), /*#__PURE__*/_react.default.createElement(_Typography.default, null, t('zoom'), ":", playerReferences.getZoom()), /*#__PURE__*/_react.default.createElement(_Typography.default, null, t('image_true_size'), ":", playerReferences.getMediaTrueWidth(), ' ', "x", playerReferences.getMediaTrueHeight()), /*#__PURE__*/_react.default.createElement(_Typography.default, null, t('container_size'), ":", playerReferences.getContainerWidth(), ' ', "x", playerReferences.getContainerHeight()), /*#__PURE__*/_react.default.createElement(_Typography.default, null, t('image_displayed'), ":", playerReferences.getDisplayedMediaWidth(), ' ', "x", playerReferences.getDisplayedMediaHeight())));
94
94
  }
@@ -55,7 +55,7 @@ function AnnotationFormOverlayTool({
55
55
  activeTool: _KonvaUtils.SHAPES_TOOL.RECTANGLE
56
56
  });
57
57
  }
58
- return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, toolState.activeTool === _KonvaUtils.OVERLAY_TOOL.EDIT && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, currentShape && displayMode === _KonvaUtils.KONVA_MODE.DRAW && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Typography.default, {
58
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, toolState.activeTool === _KonvaUtils.OVERLAY_TOOL.EDIT && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, (currentShape && displayMode === _KonvaUtils.KONVA_MODE.DRAW || currentShape && displayMode === _KonvaUtils.KONVA_MODE.TARGET) && /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement(_Typography.default, {
59
59
  variant: "subFormSectionTitle"
60
60
  }, t('selected_object')), /*#__PURE__*/_react.default.createElement(_AnnotationFormOverlayToolOptions.default, {
61
61
  t: t,
@@ -72,7 +72,8 @@ function AnnotationFormOverlayTool({
72
72
  text: currentShape.text
73
73
  },
74
74
  setToolState: customUpdateToolState,
75
- displayMode: displayMode
75
+ displayMode: displayMode,
76
+ currentShape: currentShape
76
77
  })), displayMode === _KonvaUtils.KONVA_MODE.DRAW && shapes.length > 0 && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_Typography.default, {
77
78
  variant: "subFormSectionTitle"
78
79
  }, t('object_list')), /*#__PURE__*/_react.default.createElement(_ShapesList.default, {
@@ -136,7 +136,7 @@ function AnnotationFormOverlayToolOptions({
136
136
  text
137
137
  });
138
138
  };
139
- return /*#__PURE__*/_react.default.createElement("div", null, displayMode === _KonvaUtils.KONVA_MODE.DRAW && (0, _KonvaUtils.isShapesTool)(toolState.activeTool) && /*#__PURE__*/_react.default.createElement(_material.Grid, {
139
+ return /*#__PURE__*/_react.default.createElement("div", null, (displayMode === _KonvaUtils.KONVA_MODE.DRAW || displayMode === _KonvaUtils.KONVA_MODE.TARGET) && (0, _KonvaUtils.isShapesTool)(toolState.activeTool) && /*#__PURE__*/_react.default.createElement(_material.Grid, {
140
140
  container: true
141
141
  }, /*#__PURE__*/_react.default.createElement(_ColorPicker.default, {
142
142
  currentColor: currentColor,
@@ -72,6 +72,7 @@ function Rectangle({
72
72
  }
73
73
  Rectangle.propTypes = {
74
74
  activeTool: _propTypes.default.string.isRequired,
75
+ baseStrokeWidth: _propTypes.default.number.isRequired,
75
76
  displayMode: _propTypes.default.string.isRequired,
76
77
  handleDragEnd: _propTypes.default.func.isRequired,
77
78
  handleDragStart: _propTypes.default.func.isRequired,
@@ -3,9 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.TEMPLATE_TYPES = exports.TEMPLATE = exports.TARGET_VIEW = exports.TAG_VIEW = exports.StyledToggleButtonGroup = exports.OVERLAY_VIEW = exports.MEDIA_TYPES = exports.MANIFEST_LINK_VIEW = exports.IMAGE_TOOL_STATE = exports.DEFAULT_TOOL_STATE = void 0;
7
- exports.getTargetSVGToolState = getTargetSVGToolState;
8
- exports.isValidUrl = exports.getTemplateType = void 0;
6
+ exports.isValidUrl = exports.getTemplateType = exports.TEMPLATE_TYPES = exports.TEMPLATE = exports.TARGET_VIEW = exports.TARGET_TOOL_STATE = exports.TAG_VIEW = exports.StyledToggleButtonGroup = exports.OVERLAY_VIEW = exports.MEDIA_TYPES = exports.MANIFEST_LINK_VIEW = exports.IMAGE_TOOL_STATE = exports.DEFAULT_TOOL_STATE = void 0;
9
7
  exports.saveAnnotationInStorageAdapter = saveAnnotationInStorageAdapter;
10
8
  exports.secondsToHMSarray = secondsToHMSarray;
11
9
  var _TextFields = _interopRequireDefault(require("@mui/icons-material/TextFields"));
@@ -158,22 +156,18 @@ const IMAGE_TOOL_STATE = exports.IMAGE_TOOL_STATE = {
158
156
 
159
157
  /**
160
158
  * Specific Tool state for the target SVG
161
- * @param imageZoom
162
- * @returns {{activeTool: string, closedMode: string, image: {id: null}, imageEvent: null,
163
- * strokeColor: string, strokeWidth: number}}
164
159
  */
165
- function getTargetSVGToolState() {
166
- return {
167
- activeTool: _KonvaUtils.OVERLAY_TOOL.SHAPE,
168
- closedMode: 'closed',
169
- image: {
170
- id: null
171
- },
172
- imageEvent: null,
173
- strokeColor: 'rgba(255,0, 0, 0.5)',
174
- strokeWidth: 5
175
- };
176
- }
160
+ const TARGET_TOOL_STATE = exports.TARGET_TOOL_STATE = {
161
+ activeTool: _KonvaUtils.OVERLAY_TOOL.SHAPE,
162
+ closedMode: 'closed',
163
+ fillColor: 'rgba(100,100,100, 0)',
164
+ image: {
165
+ id: null
166
+ },
167
+ imageEvent: null,
168
+ strokeColor: 'rgba(255,0, 0, 0.5)',
169
+ strokeWidth: 5
170
+ };
177
171
  const TARGET_VIEW = exports.TARGET_VIEW = 'target';
178
172
  const OVERLAY_VIEW = exports.OVERLAY_VIEW = 'layer';
179
173
  const TAG_VIEW = exports.TAG_VIEW = 'tag';
@@ -11,8 +11,8 @@ var _AnnotationFormFooter = _interopRequireDefault(require("./AnnotationFormFoot
11
11
  var _AnnotationFormUtils = require("./AnnotationFormUtils");
12
12
  var _TargetFormSection = _interopRequireDefault(require("./TargetFormSection"));
13
13
  var _KonvaUtils = require("./AnnotationFormOverlay/KonvaDrawing/KonvaUtils");
14
- var _TextFormSection = _interopRequireDefault(require("./TextFormSection"));
15
14
  var _MultiTagsInput = require("./MultiTagsInput");
15
+ var _TextCommentInput = require("./TextCommentInput");
16
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
17
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
18
18
  function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
@@ -39,7 +39,7 @@ function MultipleBodyTemplate({
39
39
  textBody: {
40
40
  purpose: 'describing',
41
41
  type: 'TextualBody',
42
- value: commentTemplate
42
+ value: ''
43
43
  }
44
44
  },
45
45
  motivation: 'commenting',
@@ -106,9 +106,10 @@ function MultipleBodyTemplate({
106
106
  spacing: 2
107
107
  }, /*#__PURE__*/_react.default.createElement(_material.Grid, {
108
108
  item: true
109
- }, /*#__PURE__*/_react.default.createElement(_TextFormSection.default, {
110
- annoHtml: annotationState.maeData.textBody.value,
111
- updateAnnotationBody: updateAnnotationTextualBodyValue,
109
+ }, /*#__PURE__*/_react.default.createElement(_TextCommentInput.TextCommentInput, {
110
+ commentTemplates: commentTemplate,
111
+ comment: annotationState.maeData.textBody.value,
112
+ setComment: updateAnnotationTextualBodyValue,
112
113
  t: t
113
114
  })), /*#__PURE__*/_react.default.createElement(_material.Grid, {
114
115
  item: true
@@ -24,7 +24,7 @@ function TargetSpatialInput({
24
24
  windowId
25
25
  }) {
26
26
  // TODO the targetSVGToolSTate is not used. Why the defaultToolState is used?
27
- const [toolState, setToolState] = (0, _react.useState)((0, _AnnotationFormUtils.getTargetSVGToolState)());
27
+ const [toolState, setToolState] = (0, _react.useState)(_AnnotationFormUtils.TARGET_TOOL_STATE);
28
28
  const [viewTool, setViewTool] = (0, _react.useState)(_AnnotationFormUtils.TARGET_VIEW);
29
29
  const [scale, setScale] = (0, _react.useState)(playerReferences.getScale());
30
30
  /** Change scale from container / canva */
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.TextCommentInput = TextCommentInput;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ var _material = require("@mui/material");
10
+ var _creatable = _interopRequireDefault(require("react-select/creatable"));
11
+ var _TextEditor = _interopRequireDefault(require("../TextEditor"));
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ /**
14
+ * TextCommentInput component
15
+ * @param commentTemplates - The list of comment templates
16
+ * @param comment - The current comment
17
+ * @param setComment - Function to set the comment
18
+ * @param t - Translation function
19
+ * @constructor
20
+ */
21
+ function TextCommentInput({
22
+ commentTemplates,
23
+ comment,
24
+ setComment,
25
+ t
26
+ }) {
27
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_material.Grid, {
28
+ container: true,
29
+ item: true
30
+ }, /*#__PURE__*/_react.default.createElement(_material.Typography, {
31
+ variant: "formSectionTitle"
32
+ }, t('note'))), commentTemplates.length > 0 && /*#__PURE__*/_react.default.createElement(_material.Grid, {
33
+ item: true,
34
+ style: {
35
+ marginBottom: '10px'
36
+ }
37
+ }, /*#__PURE__*/_react.default.createElement(_creatable.default, {
38
+ options: commentTemplates.map(template => ({
39
+ label: template.title,
40
+ value: template.content,
41
+ title: template.content // Add title attribute for tooltip
42
+ })),
43
+ placeholder: t('useTemplate'),
44
+ onChange: selectedOption => {
45
+ if (selectedOption) {
46
+ setComment(selectedOption.value);
47
+ }
48
+ },
49
+ isClearable: true,
50
+ isSearchable: true,
51
+ formatOptionLabel: option => /*#__PURE__*/_react.default.createElement("div", {
52
+ title: option.title
53
+ }, option.label),
54
+ styles: {
55
+ marginBottom: '20px'
56
+ }
57
+ })), /*#__PURE__*/_react.default.createElement(_material.Grid, {
58
+ container: true,
59
+ item: true
60
+ }, /*#__PURE__*/_react.default.createElement(_TextEditor.default, {
61
+ text: comment,
62
+ setText: setComment
63
+ })));
64
+ }
65
+ TextCommentInput.propTypes = {
66
+ comment: _propTypes.default.string.isRequired,
67
+ // eslint-disable-next-line react/forbid-prop-types
68
+ commentTemplates: _propTypes.default.arrayOf(_propTypes.default.object).isRequired,
69
+ setComment: _propTypes.default.func.isRequired,
70
+ t: _propTypes.default.func.isRequired
71
+ };
@@ -87,6 +87,7 @@ const en = exports.en = {
87
87
  textual_note_with_target: 'Textual note with target',
88
88
  tool_selection: 'Tool Selection',
89
89
  unsupported_media_message: 'Your current canvas media type is not supported by the annotation editor.',
90
+ useTemplate: 'Use a template',
90
91
  video_annotation_instruction: 'If you want to annotate video media, you must install MAEV to create and edit annotations on video:',
91
92
  your_tag_here: 'Your tag here:',
92
93
  zoom: 'Zoom'
@@ -87,6 +87,7 @@ const fr = exports.fr = {
87
87
  textual_note_with_target: 'Note textuelle avec cible',
88
88
  tool_selection: 'Sélection d’outil',
89
89
  unsupported_media_message: 'Le type de média de votre canvas actuel n\'est pas pris en charge par l\'éditeur d\'annotations.',
90
+ useTemplate: 'Utiliser un modèle',
90
91
  video_annotation_instruction: 'Si vous souhaitez annoter des vidéos, vous devez installer le plugin Mirador Annotation Editor Video (MAEV) pour créer et modifier des annotations sur les vidéos :',
91
92
  your_tag_here: 'Votre étiquette',
92
93
  zoom: 'Zoom'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mirador-annotation-editor-video",
3
- "version": "1.0.99",
3
+ "version": "1.1.1",
4
4
  "description": "Mirador annotation editor video plugin in a React component. Mirador 4 (Alpha 2) compatible ",
5
5
  "main": "es/index.js",
6
6
  "module": "es/index.js",
@@ -63,7 +63,7 @@
63
63
  "prop-types": "^15.7.2",
64
64
  "react": "^18.2.0",
65
65
  "react-dom": "^18.0.0",
66
- "uuid": "^8.0.0"
66
+ "uuid": "^11.0.0"
67
67
  },
68
68
  "devDependencies": {
69
69
  "@babel/cli": "^7.25.9",
package/src/IIIFUtils.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  OVERLAY_TOOL,
6
6
  SHAPES_TOOL,
7
7
  } from './annotationForm/AnnotationFormOverlay/KonvaDrawing/KonvaUtils';
8
- import { TEMPLATE } from './annotationForm/AnnotationFormUtils';
8
+ import { TARGET_TOOL_STATE, TEMPLATE } from './annotationForm/AnnotationFormUtils';
9
9
 
10
10
  /**
11
11
  * Check if annotation is exportable to image in case of Konva annotation
@@ -136,8 +136,7 @@ export const getIIIFTargetFromMaeData = (
136
136
  case TEMPLATE.MULTIPLE_BODY_TYPE:
137
137
  case TEMPLATE.TEXT_TYPE:
138
138
  // In some case the target can be simplified in a string
139
- if (maeTarget.drawingState.shapes.length === 1
140
- && maeTarget.drawingState.shapes[0].type === SHAPES_TOOL.RECTANGLE) {
139
+ if (isSimpleTarget(maeTarget.drawingState.shapes)) {
141
140
  return getIIIFTargetFromRectangleShape(
142
141
  maeTarget,
143
142
  canvasId,
@@ -155,6 +154,11 @@ export const getIIIFTargetFromMaeData = (
155
154
  return getIIIFTargetFullCanvas(maeData, canvasId);
156
155
  };
157
156
 
157
+ const isSimpleTarget = (shapes) => shapes.length === 1
158
+ && shapes[0].type === SHAPES_TOOL.RECTANGLE
159
+ && shapes[0].strokeColor === TARGET_TOOL_STATE.strokeColor
160
+ && shapes[0].fillColor === TARGET_TOOL_STATE.fillColor;
161
+
158
162
  /**
159
163
  * Get the IIIF target from a Konva annotation (Drawing template)
160
164
  * @param maeData
package/src/TextEditor.js CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useState } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import ReactQuill from 'react-quill';
4
4
  import 'react-quill/dist/quill.snow.css';
@@ -12,20 +12,15 @@ const StyledReactQuill = styled(ReactQuill)(({ theme }) => ({
12
12
 
13
13
  /** Rich text editor for annotation body */
14
14
  function TextEditor({
15
- annoHtml,
16
- updateAnnotationBody,
15
+ text,
16
+ setText,
17
17
  }) {
18
- const [editorHtml, setEditorHtml] = useState(annoHtml);
19
-
20
18
  /**
21
19
  * Handle Change On ReactQuil Editor
22
20
  * @param html
23
21
  */
24
22
  const handleChange = (html) => {
25
- setEditorHtml(html);
26
- if (updateAnnotationBody) {
27
- updateAnnotationBody(html);
28
- }
23
+ setText(html);
29
24
  };
30
25
  const modules = {
31
26
  toolbar: [
@@ -63,7 +58,7 @@ function TextEditor({
63
58
  return (
64
59
  <div data-text-editor="name">
65
60
  <StyledReactQuill
66
- value={editorHtml}
61
+ value={text}
67
62
  onChange={handleChange}
68
63
  placeholder="Your text here"
69
64
  bounds='[data-text-editor="name"]'
@@ -75,8 +70,8 @@ function TextEditor({
75
70
  }
76
71
 
77
72
  TextEditor.propTypes = {
78
- annoHtml: PropTypes.string.isRequired,
79
- updateAnnotationBody: PropTypes.func.isRequired,
73
+ setText: PropTypes.func.isRequired,
74
+ text: PropTypes.string.isRequired,
80
75
  };
81
76
 
82
77
  export default TextEditor;
@@ -112,7 +112,7 @@ export default function AnnotationFormBody(
112
112
  saveAnnotation={saveAnnotation}
113
113
  t={t}
114
114
  windowId={windowId}
115
- commentTemplate={config?.annotation?.commentTemplate ?? ''}
115
+ commentTemplate={config?.annotation?.commentTemplates ?? []}
116
116
  tagsSuggestions={config?.annotation?.tagsSuggestions ?? []}
117
117
  />
118
118
  )}
@@ -58,7 +58,8 @@ function AnnotationFormOverlayTool({
58
58
  toolState.activeTool === OVERLAY_TOOL.EDIT && (
59
59
  <>
60
60
  {
61
- currentShape && displayMode === KONVA_MODE.DRAW && (
61
+ ((currentShape && displayMode === KONVA_MODE.DRAW)
62
+ || (currentShape && displayMode === KONVA_MODE.TARGET)) && (
62
63
  <div>
63
64
  <Typography variant="subFormSectionTitle">
64
65
  {t('selected_object')}
@@ -77,6 +78,7 @@ function AnnotationFormOverlayTool({
77
78
  }}
78
79
  setToolState={customUpdateToolState}
79
80
  displayMode={displayMode}
81
+ currentShape={currentShape}
80
82
  />
81
83
  </div>
82
84
  )
@@ -139,7 +139,7 @@ function AnnotationFormOverlayToolOptions({
139
139
  return (
140
140
  <div>
141
141
  {
142
- (displayMode === KONVA_MODE.DRAW
142
+ ((displayMode === KONVA_MODE.DRAW || displayMode === KONVA_MODE.TARGET)
143
143
  && isShapesTool(toolState.activeTool)) && (
144
144
  <Grid container>
145
145
  <ColorPicker
@@ -73,6 +73,7 @@ function Rectangle({
73
73
 
74
74
  Rectangle.propTypes = {
75
75
  activeTool: PropTypes.string.isRequired,
76
+ baseStrokeWidth: PropTypes.number.isRequired,
76
77
  displayMode: PropTypes.string.isRequired,
77
78
  handleDragEnd: PropTypes.func.isRequired,
78
79
  handleDragStart: PropTypes.func.isRequired,
@@ -145,20 +145,16 @@ export const IMAGE_TOOL_STATE = {
145
145
 
146
146
  /**
147
147
  * Specific Tool state for the target SVG
148
- * @param imageZoom
149
- * @returns {{activeTool: string, closedMode: string, image: {id: null}, imageEvent: null,
150
- * strokeColor: string, strokeWidth: number}}
151
148
  */
152
- export function getTargetSVGToolState() {
153
- return {
154
- activeTool: OVERLAY_TOOL.SHAPE,
155
- closedMode: 'closed',
156
- image: { id: null },
157
- imageEvent: null,
158
- strokeColor: 'rgba(255,0, 0, 0.5)',
159
- strokeWidth: 5,
160
- };
161
- }
149
+ export const TARGET_TOOL_STATE = {
150
+ activeTool: OVERLAY_TOOL.SHAPE,
151
+ closedMode: 'closed',
152
+ fillColor: 'rgba(100,100,100, 0)',
153
+ image: { id: null },
154
+ imageEvent: null,
155
+ strokeColor: 'rgba(255,0, 0, 0.5)',
156
+ strokeWidth: 5,
157
+ };
162
158
 
163
159
  export const TARGET_VIEW = 'target';
164
160
  export const OVERLAY_VIEW = 'layer';
@@ -28,9 +28,6 @@ export function MultiTagsInput({
28
28
  <Typography variant="formSectionTitle">
29
29
  {t('tags')}
30
30
  </Typography>
31
- {/* Show list of suggestions into a clickable tag */}
32
- {/* add a toggle to show hide suggestions */}
33
-
34
31
  <CreatableSelect
35
32
  isMulti
36
33
  options={mappedSuggestionsTags}
@@ -39,11 +36,9 @@ export function MultiTagsInput({
39
36
  closeMenuOnSelect={false}
40
37
  placeholder={t('tagsPlaceholder')}
41
38
  />
42
-
43
39
  <Divider
44
40
  spacing={2}
45
41
  />
46
-
47
42
  </>
48
43
  );
49
44
  }
@@ -5,8 +5,8 @@ import AnnotationFormFooter from './AnnotationFormFooter';
5
5
  import { TEMPLATE } from './AnnotationFormUtils';
6
6
  import TargetFormSection from './TargetFormSection';
7
7
  import { resizeKonvaStage } from './AnnotationFormOverlay/KonvaDrawing/KonvaUtils';
8
- import TextFormSection from './TextFormSection';
9
8
  import { MultiTagsInput } from './MultiTagsInput';
9
+ import { TextCommentInput } from './TextCommentInput';
10
10
 
11
11
  /** Tagging Template* */
12
12
  export default function MultipleBodyTemplate(
@@ -34,7 +34,7 @@ export default function MultipleBodyTemplate(
34
34
  textBody: {
35
35
  purpose: 'describing',
36
36
  type: 'TextualBody',
37
- value: commentTemplate,
37
+ value: '',
38
38
  },
39
39
  },
40
40
  motivation: 'commenting',
@@ -108,9 +108,10 @@ export default function MultipleBodyTemplate(
108
108
  return (
109
109
  <Grid container direction="column" spacing={2}>
110
110
  <Grid item>
111
- <TextFormSection
112
- annoHtml={annotationState.maeData.textBody.value}
113
- updateAnnotationBody={updateAnnotationTextualBodyValue}
111
+ <TextCommentInput
112
+ commentTemplates={commentTemplate}
113
+ comment={annotationState.maeData.textBody.value}
114
+ setComment={updateAnnotationTextualBodyValue}
114
115
  t={t}
115
116
  />
116
117
  </Grid>
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
3
3
  import Typography from '@mui/material/Typography';
4
4
  import { Grid } from '@mui/material';
5
5
  import AnnotationDrawing from './AnnotationFormOverlay/AnnotationDrawing';
6
- import { getTargetSVGToolState, TARGET_VIEW } from './AnnotationFormUtils';
6
+ import { TARGET_TOOL_STATE, TARGET_VIEW } from './AnnotationFormUtils';
7
7
  import AnnotationFormOverlay from './AnnotationFormOverlay/AnnotationFormOverlay';
8
8
  import { KONVA_MODE } from './AnnotationFormOverlay/KonvaDrawing/KonvaUtils';
9
9
 
@@ -16,7 +16,7 @@ export function TargetSpatialInput({
16
16
  windowId,
17
17
  }) {
18
18
  // TODO the targetSVGToolSTate is not used. Why the defaultToolState is used?
19
- const [toolState, setToolState] = useState(getTargetSVGToolState());
19
+ const [toolState, setToolState] = useState(TARGET_TOOL_STATE);
20
20
  const [viewTool, setViewTool] = useState(TARGET_VIEW);
21
21
  const [scale, setScale] = useState(playerReferences.getScale());
22
22
  /** Change scale from container / canva */
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Grid, Typography } from '@mui/material';
4
+ import CreatableSelect from 'react-select/creatable';
5
+ import TextEditor from '../TextEditor';
6
+
7
+ /**
8
+ * TextCommentInput component
9
+ * @param commentTemplates - The list of comment templates
10
+ * @param comment - The current comment
11
+ * @param setComment - Function to set the comment
12
+ * @param t - Translation function
13
+ * @constructor
14
+ */
15
+ export function TextCommentInput({
16
+ commentTemplates,
17
+ comment,
18
+ setComment,
19
+ t,
20
+ }) {
21
+ return (
22
+ <>
23
+ <Grid container item>
24
+ <Typography variant="formSectionTitle">
25
+ {t('note')}
26
+ </Typography>
27
+ </Grid>
28
+ {commentTemplates.length > 0 && (
29
+ <Grid item style={{ marginBottom: '10px' }}>
30
+ <CreatableSelect
31
+ options={commentTemplates.map((template) => ({
32
+ label: template.title,
33
+ value: template.content,
34
+ title: template.content, // Add title attribute for tooltip
35
+ }))}
36
+ placeholder={t('useTemplate')}
37
+ onChange={(selectedOption) => {
38
+ if (selectedOption) {
39
+ setComment(selectedOption.value);
40
+ }
41
+ }}
42
+ isClearable
43
+ isSearchable
44
+ formatOptionLabel={(option) => (
45
+ <div title={option.title}>
46
+ {option.label}
47
+ </div>
48
+ )}
49
+ styles={{
50
+ marginBottom: '20px',
51
+ }}
52
+ />
53
+ </Grid>
54
+ )}
55
+
56
+ <Grid container item>
57
+ <TextEditor
58
+ text={comment}
59
+ setText={setComment}
60
+ />
61
+ </Grid>
62
+ </>
63
+ );
64
+ }
65
+
66
+ TextCommentInput.propTypes = {
67
+ comment: PropTypes.string.isRequired,
68
+ // eslint-disable-next-line react/forbid-prop-types
69
+ commentTemplates: PropTypes.arrayOf(PropTypes.object).isRequired,
70
+ setComment: PropTypes.func.isRequired,
71
+ t: PropTypes.func.isRequired,
72
+ };
@@ -81,6 +81,7 @@ export const en = {
81
81
  textual_note_with_target: 'Textual note with target',
82
82
  tool_selection: 'Tool Selection',
83
83
  unsupported_media_message: 'Your current canvas media type is not supported by the annotation editor.',
84
+ useTemplate: 'Use a template',
84
85
  video_annotation_instruction: 'If you want to annotate video media, you must install MAEV to create and edit annotations on video:',
85
86
  your_tag_here: 'Your tag here:',
86
87
  zoom: 'Zoom',
@@ -81,6 +81,7 @@ export const fr = {
81
81
  textual_note_with_target: 'Note textuelle avec cible',
82
82
  tool_selection: 'Sélection d’outil',
83
83
  unsupported_media_message: 'Le type de média de votre canvas actuel n\'est pas pris en charge par l\'éditeur d\'annotations.',
84
+ useTemplate: 'Utiliser un modèle',
84
85
  video_annotation_instruction: 'Si vous souhaitez annoter des vidéos, vous devez installer le plugin Mirador Annotation Editor Video (MAEV) pour créer et modifier des annotations sur les vidéos :',
85
86
  your_tag_here: 'Votre étiquette',
86
87
  zoom: 'Zoom',