@searpent/react-image-annotate 2.0.74 → 2.0.76
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/.babelrc +6 -0
- package/.env +1 -0
- package/.flowconfig +2 -0
- package/.github/workflows/release-on-master.yml +32 -0
- package/.github/workflows/test.yml +16 -0
- package/.prettierrc +3 -0
- package/.releaserc.js +18 -0
- package/.storybook/addons.js +2 -0
- package/.storybook/config.js +16 -0
- package/LICENSE +21 -0
- package/package.json +1 -1
- package/public/favicon.ico +0 -0
- package/public/index.html +38 -0
- package/src/Annotator/bike-pic.png +0 -0
- package/src/Annotator/bike-pic2.png +0 -0
- package/src/Annotator/dab-keyframes.story.json +1 -0
- package/src/Annotator/exampleImages.js +48 -0
- package/src/Annotator/examplePhotos.js +7603 -0
- package/src/Annotator/index.js +380 -0
- package/src/Annotator/index.story.js +877 -0
- package/src/Annotator/poses.story.js +150 -0
- package/src/Annotator/reducers/combine-reducers.js +7 -0
- package/src/Annotator/reducers/convert-expanding-line-to-polygon.js +53 -0
- package/{Annotator → src/Annotator}/reducers/fix-twisted.js +5 -3
- package/src/Annotator/reducers/general-reducer.js +1228 -0
- package/src/Annotator/reducers/get-active-image.js +21 -0
- package/src/Annotator/reducers/get-implied-video-regions.js +115 -0
- package/src/Annotator/reducers/history-handler.js +60 -0
- package/src/Annotator/reducers/image-reducer.js +23 -0
- package/src/Annotator/reducers/video-reducer.js +85 -0
- package/src/Annotator/video.story.js +51 -0
- package/src/ClassSelectionMenu/index.js +112 -0
- package/src/Crosshairs/index.js +64 -0
- package/src/DebugSidebarBox/index.js +36 -0
- package/src/DemoSite/Editor.js +235 -0
- package/src/DemoSite/ErrorBoundaryDialog.js +34 -0
- package/src/DemoSite/index.js +41 -0
- package/src/DemoSite/index.story.js +10 -0
- package/src/DemoSite/simple-segmentation-example.json +572 -0
- package/{Editor → src/Editor}/annotation-plugin/annotation.css +2 -0
- package/src/Editor/annotation-plugin/annotation.js +536 -0
- package/src/Editor/index.js +50 -0
- package/src/Editor/readOnly.js +21 -0
- package/{Editor → src/Editor}/tools.js +3 -2
- package/src/Errorer/index.js +13 -0
- package/src/FullImageSegmentationAnnotator/hard1.story.jpg +0 -0
- package/src/FullImageSegmentationAnnotator/hard2.story.jpg +0 -0
- package/src/FullImageSegmentationAnnotator/hard3.story.jpg +0 -0
- package/src/FullImageSegmentationAnnotator/index.js +7 -0
- package/src/FullImageSegmentationAnnotator/index.story.js +177 -0
- package/src/FullImageSegmentationAnnotator/orange.story.png +0 -0
- package/src/GroupSelectorSidebarBox/index.js +48 -0
- package/src/GroupsEditorSidebarBox/index.js +108 -0
- package/src/HelpSidebarBox/index.js +43 -0
- package/src/HighlightBox/index.js +143 -0
- package/src/HistorySidebarBox/index.js +78 -0
- package/src/ImageCanvas/dancing-man.story.jpg +0 -0
- package/src/ImageCanvas/index.js +515 -0
- package/src/ImageCanvas/index.story.js +314 -0
- package/src/ImageCanvas/mouse_mask.story.png +0 -0
- package/src/ImageCanvas/region-tools.js +171 -0
- package/src/ImageCanvas/seves_desk.story.jpg +0 -0
- package/{ImageCanvas → src/ImageCanvas}/styles.js +8 -12
- package/src/ImageCanvas/use-mouse.js +168 -0
- package/src/ImageCanvas/use-project-box.js +23 -0
- package/src/ImageCanvas/use-wasd-mode.js +50 -0
- package/src/ImageMask/index.js +127 -0
- package/src/ImageMask/load-image.js +32 -0
- package/src/ImageSelectorSidebarBox/index.js +54 -0
- package/src/KeyframeTimeline/get-time-string.js +25 -0
- package/src/KeyframeTimeline/index.js +223 -0
- package/src/KeyframesSelectorSidebarBox/index.js +93 -0
- package/src/LandingPage/content.md +57 -0
- package/src/LandingPage/github-markdown.css +964 -0
- package/src/LandingPage/index.js +147 -0
- package/src/Locker/index.js +13 -0
- package/src/MainLayout/RightSidebarItemsWrapper.js +21 -0
- package/src/MainLayout/icon-dictionary.js +79 -0
- package/src/MainLayout/index.js +564 -0
- package/src/MainLayout/index.story.js +240 -0
- package/{MainLayout → src/MainLayout}/styles.js +7 -6
- package/src/MainLayout/types.js +171 -0
- package/src/MainLayout/use-implied-video-regions.js +17 -0
- package/src/MetadataEditorSidebarBox/index.js +160 -0
- package/src/PageSelector/index.js +159 -0
- package/src/PointDistances/index.js +90 -0
- package/src/PreventScrollToParents/index.js +48 -0
- package/src/PreventScrollToParents/index.story.js +23 -0
- package/src/RegionLabel/index.js +236 -0
- package/{RegionLabel → src/RegionLabel}/styles.js +15 -12
- package/src/RegionSelectAndTransformBoxes/index.js +236 -0
- package/src/RegionSelectorSidebarBox/index.js +220 -0
- package/{RegionSelectorSidebarBox → src/RegionSelectorSidebarBox}/styles.js +14 -13
- package/src/RegionShapes/index.js +254 -0
- package/src/RegionTags/index.js +136 -0
- package/src/SettingsDialog/index.js +58 -0
- package/src/SettingsProvider/index.js +57 -0
- package/src/Shortcuts/ShortcutField.js +44 -0
- package/src/Shortcuts/index.js +129 -0
- package/src/ShortcutsManager/index.js +162 -0
- package/src/Sidebar/index.js +117 -0
- package/src/SidebarBoxContainer/index.js +93 -0
- package/src/SmallToolButton/index.js +57 -0
- package/src/TagsSidebarBox/index.js +93 -0
- package/src/TaskDescriptionSidebarBox/index.js +43 -0
- package/src/Theme/index.js +36 -0
- package/src/VideoOrImageCanvasBackground/index.js +170 -0
- package/src/colors.js +32 -0
- package/src/hooks/use-colors.js +75 -0
- package/src/hooks/use-event-callback.js +11 -0
- package/src/hooks/use-exclude-pattern.js +27 -0
- package/src/hooks/use-load-image.js +21 -0
- package/src/hooks/use-window-size.js +46 -0
- package/{hooks → src/hooks}/xpattern.js +1 -1
- package/src/hooks/xpattern.png +0 -0
- package/src/index.js +18 -0
- package/src/lib.js +7 -0
- package/src/screenshot.png +0 -0
- package/src/site.css +5 -0
- package/src/stories.js +2 -0
- package/src/utils/blocks-to-article.js +61 -0
- package/{utils → src/utils}/blocks-to-article.test.js +8 -5
- package/{utils → src/utils}/default-locked-until.js +1 -2
- package/{utils → src/utils}/filter-only-unique.js +1 -1
- package/src/utils/get-from-local-storage.js +7 -0
- package/src/utils/get-hotkey-help-text.js +11 -0
- package/src/utils/get-landmarks-with-transform.js +23 -0
- package/src/utils/photosToImages.js +67 -0
- package/src/utils/regions-groups.js +19 -0
- package/src/utils/regions-to-blocks.js +16 -0
- package/src/utils/saveable-actions-enum.js +5 -0
- package/src/utils/set-in-local-storage.js +6 -0
- package/src/utils/sleep.js +3 -0
- package/src/utils/uuid-to-hash.js +5 -0
- package/Annotator/exampleImages.js +0 -41
- package/Annotator/examplePhotos.js +0 -6980
- package/Annotator/index.js +0 -417
- package/Annotator/reducers/combine-reducers.js +0 -14
- package/Annotator/reducers/convert-expanding-line-to-polygon.js +0 -73
- package/Annotator/reducers/general-reducer.js +0 -1430
- package/Annotator/reducers/get-active-image.js +0 -27
- package/Annotator/reducers/get-implied-video-regions.js +0 -180
- package/Annotator/reducers/history-handler.js +0 -38
- package/Annotator/reducers/image-reducer.js +0 -20
- package/Annotator/reducers/video-reducer.js +0 -88
- package/ClassSelectionMenu/index.js +0 -140
- package/Crosshairs/index.js +0 -53
- package/DebugSidebarBox/index.js +0 -20
- package/DemoSite/Editor.js +0 -194
- package/DemoSite/ErrorBoundaryDialog.js +0 -64
- package/DemoSite/index.js +0 -40
- package/Editor/annotation-plugin/annotation.js +0 -642
- package/Editor/index.js +0 -93
- package/Editor/readOnly.js +0 -68
- package/Errorer/index.js +0 -11
- package/FullImageSegmentationAnnotator/index.js +0 -7
- package/GroupSelectorSidebarBox/index.js +0 -63
- package/GroupsEditorSidebarBox/index.js +0 -138
- package/HelpSidebarBox/index.js +0 -58
- package/HighlightBox/index.js +0 -102
- package/HistorySidebarBox/index.js +0 -71
- package/ImageCanvas/index.js +0 -441
- package/ImageCanvas/region-tools.js +0 -165
- package/ImageCanvas/use-mouse.js +0 -180
- package/ImageCanvas/use-project-box.js +0 -27
- package/ImageCanvas/use-wasd-mode.js +0 -62
- package/ImageMask/index.js +0 -133
- package/ImageMask/load-image.js +0 -25
- package/ImageSelectorSidebarBox/index.js +0 -60
- package/KeyframeTimeline/get-time-string.js +0 -27
- package/KeyframeTimeline/index.js +0 -233
- package/KeyframesSelectorSidebarBox/index.js +0 -93
- package/LandingPage/index.js +0 -159
- package/Locker/index.js +0 -11
- package/MainLayout/RightSidebarItemsWrapper.js +0 -19
- package/MainLayout/icon-dictionary.js +0 -104
- package/MainLayout/index.js +0 -526
- package/MainLayout/types.js +0 -0
- package/MainLayout/use-implied-video-regions.js +0 -13
- package/MetadataEditorSidebarBox/index.js +0 -231
- package/PageSelector/index.js +0 -180
- package/PointDistances/index.js +0 -73
- package/PreventScrollToParents/index.js +0 -51
- package/RegionLabel/index.js +0 -232
- package/RegionSelectAndTransformBoxes/index.js +0 -169
- package/RegionSelectorSidebarBox/index.js +0 -254
- package/RegionShapes/index.js +0 -294
- package/RegionTags/index.js +0 -144
- package/SettingsDialog/index.js +0 -52
- package/SettingsProvider/index.js +0 -60
- package/Shortcuts/ShortcutField.js +0 -46
- package/Shortcuts/index.js +0 -133
- package/ShortcutsManager/index.js +0 -155
- package/Sidebar/index.js +0 -69
- package/SidebarBoxContainer/index.js +0 -93
- package/SmallToolButton/index.js +0 -42
- package/TagsSidebarBox/index.js +0 -105
- package/TaskDescriptionSidebarBox/index.js +0 -58
- package/Theme/index.js +0 -30
- package/VideoOrImageCanvasBackground/index.js +0 -151
- package/colors.js +0 -14
- package/hooks/use-colors.js +0 -94
- package/hooks/use-event-callback.js +0 -10
- package/hooks/use-exclude-pattern.js +0 -24
- package/hooks/use-load-image.js +0 -26
- package/hooks/use-window-size.js +0 -46
- package/index.js +0 -3
- package/lib.js +0 -3
- package/stories.js +0 -5
- package/utils/blocks-to-article.js +0 -60
- package/utils/get-from-local-storage.js +0 -7
- package/utils/get-hotkey-help-text.js +0 -9
- package/utils/get-landmarks-with-transform.js +0 -40
- package/utils/photosToImages.js +0 -85
- package/utils/regions-groups.js +0 -28
- package/utils/regions-to-blocks.js +0 -18
- package/utils/saveable-actions-enum.js +0 -3
- package/utils/set-in-local-storage.js +0 -3
- package/utils/sleep.js +0 -7
- package/utils/uuid-to-hash.js +0 -5
- /package/{Errorer → src/Errorer}/errorer.css +0 -0
- /package/{Locker → src/Locker}/locker.css +0 -0
- /package/{PageSelector → src/PageSelector}/page-selector.css +0 -0
- /package/{utils → src/utils}/next-group-id.js +0 -0
|
@@ -0,0 +1,536 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build styles
|
|
3
|
+
*/
|
|
4
|
+
import './annotation.css';
|
|
5
|
+
|
|
6
|
+
export function whitespaceCharactersToHTML(str = '') {
|
|
7
|
+
return str
|
|
8
|
+
// new line whitespace
|
|
9
|
+
.replaceAll(/[ ]\n[ ]/gm, ' <br> ') // space both sides adds
|
|
10
|
+
.replaceAll(/\n[ ]/gm, '<br> ') // space right side adds
|
|
11
|
+
.replaceAll(/[ ]\n/gm, ' <br>') // space left side adds
|
|
12
|
+
.replaceAll(/\n/gm, '<br>') // no spaces
|
|
13
|
+
// tab whitespace
|
|
14
|
+
.replaceAll(/\t/gm, ' ') // no spaces
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function HTMLToWhitespaceCharacters(str = '') {
|
|
18
|
+
return str
|
|
19
|
+
.replaceAll(/(( |\s){4})/gm, '\t')
|
|
20
|
+
.replaceAll(' ', ' ')
|
|
21
|
+
.replaceAll('<br>', '\n') // new line whitespace
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
// Possible classes
|
|
26
|
+
// ================
|
|
27
|
+
|
|
28
|
+
class Annotation {
|
|
29
|
+
/**
|
|
30
|
+
* Render plugin`s main Element and fill it with saved data
|
|
31
|
+
*
|
|
32
|
+
* @param {{data: HeaderData, config: HeaderConfig, api: object}}
|
|
33
|
+
* data — previously saved data
|
|
34
|
+
* config - user config for Tool
|
|
35
|
+
* api - Editor.js API
|
|
36
|
+
* readOnly - read only mode flag
|
|
37
|
+
*/
|
|
38
|
+
constructor({ data, config, api, readOnly }) {
|
|
39
|
+
this.api = api;
|
|
40
|
+
this.readOnly = readOnly;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Styles
|
|
44
|
+
*
|
|
45
|
+
* @type {object}
|
|
46
|
+
*/
|
|
47
|
+
this._CSS = {
|
|
48
|
+
block: this.api.styles.block,
|
|
49
|
+
settingsButton: this.api.styles.settingsButton,
|
|
50
|
+
settingsButtonActive: this.api.styles.settingsButtonActive,
|
|
51
|
+
wrapper: 'ce-header',
|
|
52
|
+
backgroundColor: (data?.highlighted && data?.clsColor) ? data.clsColor : undefined,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Tool's settings passed from Editor
|
|
57
|
+
*
|
|
58
|
+
* @type {HeaderConfig}
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
this._settings = config;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Block's data
|
|
65
|
+
*
|
|
66
|
+
* @type {HeaderData}
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
this._data = this.normalizeData(data);
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* List of settings buttons
|
|
73
|
+
*
|
|
74
|
+
* @type {HTMLElement[]}
|
|
75
|
+
*/
|
|
76
|
+
this.settingsButtons = [];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Main Block wrapper
|
|
80
|
+
*
|
|
81
|
+
* @type {HTMLElement}
|
|
82
|
+
* @private
|
|
83
|
+
*/
|
|
84
|
+
this._element = this.getTag();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Normalize input data
|
|
89
|
+
*
|
|
90
|
+
* @param {HeaderData} data - saved data to process
|
|
91
|
+
*
|
|
92
|
+
* @returns {HeaderData}
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
normalizeData(data) {
|
|
96
|
+
const newData = {};
|
|
97
|
+
|
|
98
|
+
if (typeof data !== 'object') {
|
|
99
|
+
data = {};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
newData.text = whitespaceCharactersToHTML(data.text)
|
|
103
|
+
newData.labelName = data.labelName || this.defaultLabel.labelName;
|
|
104
|
+
|
|
105
|
+
return newData;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Return Tool's view
|
|
110
|
+
*
|
|
111
|
+
* @returns {HTMLHeadingElement}
|
|
112
|
+
* @public
|
|
113
|
+
*/
|
|
114
|
+
render() {
|
|
115
|
+
return this._element;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Create Block's settings block
|
|
120
|
+
*
|
|
121
|
+
* @returns {HTMLElement}
|
|
122
|
+
*/
|
|
123
|
+
renderSettings() {
|
|
124
|
+
const holder = document.createElement('DIV');
|
|
125
|
+
|
|
126
|
+
// do not add settings button, when only one label is configured
|
|
127
|
+
if (this.labels.length <= 1) {
|
|
128
|
+
return holder;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Add type selectors */
|
|
132
|
+
this.labels.forEach(label => {
|
|
133
|
+
const selectTypeButton = document.createElement('DIV');
|
|
134
|
+
|
|
135
|
+
selectTypeButton.classList.add(this._CSS.settingsButton);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Highlight current label button
|
|
139
|
+
*/
|
|
140
|
+
if (this.currentLabel.labelName === label.labelName) {
|
|
141
|
+
selectTypeButton.classList.add(this._CSS.settingsButtonActive);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Add SVG icon
|
|
146
|
+
*/
|
|
147
|
+
selectTypeButton.innerHTML = `${label.labelName}`;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Save label to its button
|
|
151
|
+
*/
|
|
152
|
+
selectTypeButton.dataset.labelName = label.labelName;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Set up click handler
|
|
156
|
+
*/
|
|
157
|
+
selectTypeButton.addEventListener('click', () => {
|
|
158
|
+
this.setLabelName(label.labelName);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Append settings button to holder
|
|
163
|
+
*/
|
|
164
|
+
holder.appendChild(selectTypeButton);
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Save settings buttons
|
|
168
|
+
*/
|
|
169
|
+
this.settingsButtons.push(selectTypeButton);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
return holder;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Callback for Block's settings buttons
|
|
177
|
+
*
|
|
178
|
+
* @param {labelName} labelName - labelName to set
|
|
179
|
+
*/
|
|
180
|
+
setLabelName(labelName) {
|
|
181
|
+
this.data = {
|
|
182
|
+
labelName,
|
|
183
|
+
text: this.data.text
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Highlight button by selected labelName
|
|
188
|
+
*/
|
|
189
|
+
this.settingsButtons.forEach(button => {
|
|
190
|
+
button.classList.toggle(
|
|
191
|
+
this._CSS.settingsButtonActive,
|
|
192
|
+
button.dataset.labelName === labelName
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Method that specified how to merge two Text blocks.
|
|
199
|
+
* Called by Editor.js by backspace at the beginning of the Block
|
|
200
|
+
*
|
|
201
|
+
* @param {HeaderData} data - saved data to merger with current block
|
|
202
|
+
* @public
|
|
203
|
+
*/
|
|
204
|
+
merge(data) {
|
|
205
|
+
const newData = {
|
|
206
|
+
text: this.data.text + data.text,
|
|
207
|
+
labelName: this.data.labelName
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
this.data = newData;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Validate Text block data:
|
|
215
|
+
* - check for emptiness
|
|
216
|
+
*
|
|
217
|
+
* @param {HeaderData} blockData — data received after saving
|
|
218
|
+
* @returns {boolean} false if saved data is not correct, otherwise true
|
|
219
|
+
* @public
|
|
220
|
+
*/
|
|
221
|
+
validate(blockData) {
|
|
222
|
+
return blockData.text.trim() !== '';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Extract Tool's data from the view
|
|
227
|
+
*
|
|
228
|
+
* @param {HTMLHeadingElement} toolsContent - Text tools rendered view
|
|
229
|
+
* @returns {HeaderData} - saved data
|
|
230
|
+
* @public
|
|
231
|
+
*/
|
|
232
|
+
save(toolsContent) {
|
|
233
|
+
return {
|
|
234
|
+
text: HTMLToWhitespaceCharacters(toolsContent.innerHTML),
|
|
235
|
+
labelName: this.currentLabel.labelName
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Allow Header to be converted to/from other blocks
|
|
241
|
+
*/
|
|
242
|
+
static get conversionConfig() {
|
|
243
|
+
return {
|
|
244
|
+
export: 'text', // use 'text' property for other blocks
|
|
245
|
+
import: 'text' // fill 'text' property from other block's export string
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Sanitizer Rules
|
|
251
|
+
*/
|
|
252
|
+
static get sanitize() {
|
|
253
|
+
return {
|
|
254
|
+
labelName: false,
|
|
255
|
+
text: {}
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Returns true to notify core that read-only is supported
|
|
261
|
+
*
|
|
262
|
+
* @returns {boolean}
|
|
263
|
+
*/
|
|
264
|
+
static get isReadOnlySupported() {
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get current Tools`s data
|
|
270
|
+
*
|
|
271
|
+
* @returns {HeaderData} Current data
|
|
272
|
+
* @private
|
|
273
|
+
*/
|
|
274
|
+
get data() {
|
|
275
|
+
this._data.text = this._element.innerHTML;
|
|
276
|
+
this._data.labelName = this.currentLabel.labelName;
|
|
277
|
+
|
|
278
|
+
return this._data;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Store data in plugin:
|
|
283
|
+
* - at the this._data property
|
|
284
|
+
* - at the HTML
|
|
285
|
+
*
|
|
286
|
+
* @param {HeaderData} data — data to set
|
|
287
|
+
* @private
|
|
288
|
+
*/
|
|
289
|
+
set data(data) {
|
|
290
|
+
this._data = this.normalizeData(data);
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* If labelName is set and block in DOM
|
|
294
|
+
* then replace it to a new block
|
|
295
|
+
*/
|
|
296
|
+
if (data.labelName !== undefined && this._element.parentNode) {
|
|
297
|
+
/**
|
|
298
|
+
* Create a new tag
|
|
299
|
+
*
|
|
300
|
+
* @type {HTMLHeadingElement}
|
|
301
|
+
*/
|
|
302
|
+
const newHeader = this.getTag();
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Save Block's content
|
|
306
|
+
*/
|
|
307
|
+
newHeader.innerHTML = this._element.innerHTML;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Replace blocks
|
|
311
|
+
*/
|
|
312
|
+
this._element.parentNode.replaceChild(newHeader, this._element);
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Save new block to private variable
|
|
316
|
+
*
|
|
317
|
+
* @type {HTMLHeadingElement}
|
|
318
|
+
* @private
|
|
319
|
+
*/
|
|
320
|
+
this._element = newHeader;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* If data.text was passed then update block's content
|
|
325
|
+
*/
|
|
326
|
+
if (data.text !== undefined) {
|
|
327
|
+
this._element.innerHTML = this._data.text || '';
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Get tag for target label
|
|
333
|
+
* By default returns second-labelled header
|
|
334
|
+
*
|
|
335
|
+
* @returns {HTMLElement}
|
|
336
|
+
*/
|
|
337
|
+
getTag() {
|
|
338
|
+
/**
|
|
339
|
+
* Create element for current Block's label
|
|
340
|
+
*/
|
|
341
|
+
const tag = document.createElement(this.currentLabel.tag);
|
|
342
|
+
if (this._CSS.backgroundColor) {
|
|
343
|
+
tag.style.backgroundColor = this._CSS.backgroundColor
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Add text to block
|
|
348
|
+
*/
|
|
349
|
+
tag.innerHTML = this._data.text || '';
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Add styles class
|
|
353
|
+
*/
|
|
354
|
+
tag.classList.add(this._CSS.wrapper);
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Make tag editable
|
|
358
|
+
*/
|
|
359
|
+
tag.contentEditable = this.readOnly ? 'false' : 'true';
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Add Placeholder
|
|
363
|
+
*/
|
|
364
|
+
tag.dataset.placeholder = this.api.i18n.t(this._settings.placeholder || '');
|
|
365
|
+
|
|
366
|
+
return tag;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Get current label
|
|
371
|
+
*
|
|
372
|
+
* @returns {label}
|
|
373
|
+
*/
|
|
374
|
+
get currentLabel() {
|
|
375
|
+
let label = this.labels.find(
|
|
376
|
+
labelItem => labelItem.labelName === this._data.labelName
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
if (!label) {
|
|
380
|
+
label = this.defaultLabel;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return label;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Return default label
|
|
388
|
+
*
|
|
389
|
+
* @returns {label}
|
|
390
|
+
*/
|
|
391
|
+
get defaultLabel() {
|
|
392
|
+
/**
|
|
393
|
+
* User can specify own default label value
|
|
394
|
+
*/
|
|
395
|
+
if (this._settings.defaultLabel) {
|
|
396
|
+
const userSpecified = this.labels.find(labelItem => {
|
|
397
|
+
return labelItem.labelName === this._settings.defaultLabel;
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
if (userSpecified) {
|
|
401
|
+
return userSpecified;
|
|
402
|
+
}
|
|
403
|
+
console.warn(
|
|
404
|
+
"(ง'̀-'́)ง Annotation Tool: the default label specified was not found in available labels"
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* With no additional options, there will be H2 by default
|
|
410
|
+
*
|
|
411
|
+
* @type {label}
|
|
412
|
+
*/
|
|
413
|
+
return this.labels[1];
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* @typedef {object} label
|
|
418
|
+
* @property {labelName} labelName - label labelName
|
|
419
|
+
* @property {string} tag - tag corresponds with label labelName
|
|
420
|
+
* @property {string} svg - icon
|
|
421
|
+
*/
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Available header labels
|
|
425
|
+
*
|
|
426
|
+
* @returns {label[]}
|
|
427
|
+
*/
|
|
428
|
+
get labels() {
|
|
429
|
+
const availableLabels = [
|
|
430
|
+
{
|
|
431
|
+
labelName: 'title',
|
|
432
|
+
tag: 'h1',
|
|
433
|
+
name: 'title',
|
|
434
|
+
backgroundColor: '#ffcccc'
|
|
435
|
+
},
|
|
436
|
+
{ labelName: 'subtitle', tag: 'h2', name: 'subtitle', backgroundColor: '#ffccff' },
|
|
437
|
+
{ labelName: 'text', tag: 'p', name: 'text', backgroundColor: '#14deef' },
|
|
438
|
+
{ labelName: 'author', tag: 'i', name: 'author', backgroundColor: '#00bb00' },
|
|
439
|
+
{ labelName: 'appendix', tag: 'p', name: 'appendix', backgroundColor: '#dcfcec' },
|
|
440
|
+
{ labelName: 'photo_author', tag: 'p', name: 'photo_author', backgroundColor: '#bb0000' },
|
|
441
|
+
{ labelName: 'photo_caption', tag: 'p', name: 'photo_caption', backgroundColor: '#ccffff' },
|
|
442
|
+
{ labelName: 'advertisement', tag: 'p', name: 'advertisement', backgroundColor: '#ffeccc' },
|
|
443
|
+
{ labelName: 'other_graphics', tag: 'p', name: 'other_graphics', backgroundColor: '#ff5400' },
|
|
444
|
+
{ labelName: 'unknown', tag: 's', name: 'unknown', backgroundColor: '#cccccc' },
|
|
445
|
+
{ labelName: 'about_author', tag: 'p', name: 'about_author', backgroundColor: '#ecffec' },
|
|
446
|
+
{ labelName: 'image', tag: 'p', name: 'image', backgroundColor: '#ffffcc' },
|
|
447
|
+
{ labelName: 'interview', tag: 'p', name: 'interview', backgroundColor: '#23b20f' },
|
|
448
|
+
{ labelName: 'table', tag: 'p', name: 'table', backgroundColor: '#0000bb' },
|
|
449
|
+
{ labelName: 'section', tag: 'p', name: 'section', backgroundColor: '#ffcccc' }
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
return this._settings.labels
|
|
453
|
+
? availableLabels.filter(l => this._settings.labels.includes(l.labelName))
|
|
454
|
+
: availableLabels;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Handle H1-H6 tags on paste to substitute it with header Tool
|
|
459
|
+
*
|
|
460
|
+
* @param {PasteEvent} event - event with pasted content
|
|
461
|
+
*/
|
|
462
|
+
onPaste(event) {
|
|
463
|
+
const content = event.detail.data;
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Define default label value
|
|
467
|
+
*
|
|
468
|
+
* @type {labelName}
|
|
469
|
+
*/
|
|
470
|
+
let { labelName } = this.defaultLabel;
|
|
471
|
+
|
|
472
|
+
switch (content.tagName) {
|
|
473
|
+
case 'H1':
|
|
474
|
+
labelName = 1;
|
|
475
|
+
break;
|
|
476
|
+
case 'H2':
|
|
477
|
+
labelName = 2;
|
|
478
|
+
break;
|
|
479
|
+
case 'H3':
|
|
480
|
+
labelName = 3;
|
|
481
|
+
break;
|
|
482
|
+
case 'H4':
|
|
483
|
+
labelName = 4;
|
|
484
|
+
break;
|
|
485
|
+
case 'H5':
|
|
486
|
+
labelName = 5;
|
|
487
|
+
break;
|
|
488
|
+
case 'H6':
|
|
489
|
+
labelName = 6;
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// if (this._settings.labels) {
|
|
494
|
+
// // Fallback to nearest label when specified not available
|
|
495
|
+
// label = this._settings.labels.reduce((prevLabel, currLabel) => {
|
|
496
|
+
// return Math.abs(currLabel - label) < Math.abs(prevLabel - label)
|
|
497
|
+
// ? currLabel
|
|
498
|
+
// : prevLabel;
|
|
499
|
+
// });
|
|
500
|
+
// }
|
|
501
|
+
|
|
502
|
+
this.data = {
|
|
503
|
+
labelName,
|
|
504
|
+
text: content.innerHTML
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Used by Editor.js paste handling API.
|
|
510
|
+
* Provides configuration to handle H1-H6 tags.
|
|
511
|
+
*
|
|
512
|
+
* @returns {{handler: (function(HTMLElement): {text: string}), tags: string[]}}
|
|
513
|
+
*/
|
|
514
|
+
static get pasteConfig() {
|
|
515
|
+
return {
|
|
516
|
+
tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Get Tool toolbox settings
|
|
522
|
+
* icon - Tool icon's SVG
|
|
523
|
+
* title - title to show in toolbox
|
|
524
|
+
*
|
|
525
|
+
* @returns {{icon: string, title: string}}
|
|
526
|
+
*/
|
|
527
|
+
static get toolbox() {
|
|
528
|
+
return {
|
|
529
|
+
icon:
|
|
530
|
+
'<svg width="17" height="15" viewBox="0 0 336 276" xmlns="http://www.w3.org/2000/svg"><path d="M291 150V79c0-19-15-34-34-34H79c-19 0-34 15-34 34v42l67-44 81 72 56-29 42 30zm0 52l-43-30-56 30-81-67-66 39v23c0 19 15 34 34 34h178c17 0 31-13 34-29zM79 0h178c44 0 79 35 79 79v118c0 44-35 79-79 79H79c-44 0-79-35-79-79V79C0 35 35 0 79 0z"/></svg>',
|
|
531
|
+
title: 'Annotation'
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export default Annotation;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { createReactEditorJS } from 'react-editor-js'
|
|
3
|
+
import blocksToArticle from '../utils/blocks-to-article';
|
|
4
|
+
import { EDITOR_JS_TOOLS } from './tools'
|
|
5
|
+
import ReadOnly from './readOnly';
|
|
6
|
+
|
|
7
|
+
const ReactEditorJS = createReactEditorJS()
|
|
8
|
+
|
|
9
|
+
function Editor({ blocks = [], onChange, imageIndex, selectedFrame }) {
|
|
10
|
+
const [editMode, setEditMode] = useState(false);
|
|
11
|
+
const handleChange = async instance => {
|
|
12
|
+
const data = await instance.saver.save();
|
|
13
|
+
onChange({ imageIndex, data })
|
|
14
|
+
};
|
|
15
|
+
const toggleEditMode = () => {
|
|
16
|
+
setEditMode(prev => !prev)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (blocks.length < 1) {
|
|
20
|
+
return <div className='instructions'><h1>Click article to display text.</h1></div>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div>
|
|
25
|
+
<div className="show-metadata-wrapper">
|
|
26
|
+
<label className="switch mr-2">
|
|
27
|
+
<input id="show-metadata" type="checkbox" value={editMode} onChange={toggleEditMode} />
|
|
28
|
+
<span className="slider round"></span>
|
|
29
|
+
</label>
|
|
30
|
+
<label>Edit mode</label>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
!editMode ?
|
|
35
|
+
(<ReadOnly article={blocksToArticle(blocks)} />) :
|
|
36
|
+
(<ReactEditorJS defaultValue={{
|
|
37
|
+
blocks
|
|
38
|
+
}}
|
|
39
|
+
tools={EDITOR_JS_TOOLS}
|
|
40
|
+
onChange={handleChange}
|
|
41
|
+
enableReInitialize
|
|
42
|
+
key={selectedFrame}
|
|
43
|
+
/>)
|
|
44
|
+
}
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default Editor;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
function ReadOnly({ article }) {
|
|
4
|
+
return (<div>
|
|
5
|
+
<h1 className='ro-title' dangerouslySetInnerHTML={{ __html: article.title }} />
|
|
6
|
+
<h2 className='ro-subtitle' dangerouslySetInnerHTML={{ __html: article.subtitle }} />
|
|
7
|
+
<p className='ro-text' dangerouslySetInnerHTML={{ __html: article.text }}></p>
|
|
8
|
+
<p className='ro-author' dangerouslySetInnerHTML={{ __html: article.author }}></p>
|
|
9
|
+
<p className='ro-about-author' dangerouslySetInnerHTML={{ __html: article.about_author }}></p>
|
|
10
|
+
<p className='ro-appendix' dangerouslySetInnerHTML={{ __html: article.appendix }}></p>
|
|
11
|
+
<p className='ro-photo-author' dangerouslySetInnerHTML={{ __html: article.photo_author }}></p>
|
|
12
|
+
<p className='ro-photo-caption' dangerouslySetInnerHTML={{ __html: article.photo_caption }}></p>
|
|
13
|
+
<p className='ro-advertisment' dangerouslySetInnerHTML={{ __html: article.advertisement }}></p>
|
|
14
|
+
<p className='ro-other-graphics' dangerouslySetInnerHTML={{ __html: article.other_graphics }}></p>
|
|
15
|
+
<p className='ro-unknown' dangerouslySetInnerHTML={{ __html: article.unknown }}></p>
|
|
16
|
+
<p className='ro-table' dangerouslySetInnerHTML={{ __html: article.table }}></p>
|
|
17
|
+
<p className='ro-section' dangerouslySetInnerHTML={{ __html: article.section }}></p>
|
|
18
|
+
</div>)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default ReadOnly;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|