@searpent/react-image-annotate 2.0.1 → 2.0.4
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/package.json +4 -1
- package/src/Annotator/examplePhotos.js +7601 -0
- package/src/Annotator/index.js +83 -3
- package/src/Annotator/index.story.js +74 -25
- package/src/Annotator/reducers/general-reducer.js +99 -17
- package/src/Editor/annotation-plugin/annotation.css +46 -0
- package/src/Editor/annotation-plugin/annotation.js +515 -0
- package/src/Editor/index.js +24 -0
- package/src/Editor/tools.js +6 -0
- package/src/GroupSelectorSidebarBox/index.js +48 -0
- package/src/ImageCanvas/index.js +31 -25
- package/src/ImageCanvas/index.story.js +100 -0
- package/src/MainLayout/index.js +250 -160
- package/src/MainLayout/types.js +64 -56
- package/src/MetadataEditorSidebarBox/index.js +98 -0
- package/src/PageSelector/index.js +76 -0
- package/src/PageSelector/page-selector.css +69 -0
- package/src/RegionLabel/index.js +21 -0
- package/src/RegionShapes/index.js +34 -16
- package/src/RegionTags/index.js +7 -3
- package/src/SettingsProvider/index.js +7 -3
- package/src/hooks/use-colors.js +74 -0
- package/src/utils/filter-only-unique.js +5 -0
- package/src/utils/photosToImages.js +40 -0
- package/src/utils/regions-to-blocks.js +14 -0
package/src/Annotator/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
MainLayoutState,
|
|
7
7
|
Mode,
|
|
8
8
|
ToolEnum,
|
|
9
|
+
Metadata
|
|
9
10
|
} from "../MainLayout/types"
|
|
10
11
|
import React, { useEffect, useReducer } from "react"
|
|
11
12
|
import makeImmutable, { without } from "seamless-immutable"
|
|
@@ -56,6 +57,18 @@ hideHeader ?: boolean,
|
|
|
56
57
|
hideFullScreen ?: boolean,
|
|
57
58
|
hideSave ?: boolean,
|
|
58
59
|
onImagesChange ?: (any) => any,
|
|
60
|
+
groups ?: Array < any >,
|
|
61
|
+
onGroupSelect ?: (any) => any,
|
|
62
|
+
hideHistory ?: boolean,
|
|
63
|
+
hideNotEditingLabel ?: boolean,
|
|
64
|
+
showEditor ?: boolean,
|
|
65
|
+
showPageSelector ?: boolean,
|
|
66
|
+
clsColors ?: Object,
|
|
67
|
+
groupColors ?: Object,
|
|
68
|
+
onRecalc ?: (any) => any,
|
|
69
|
+
onSave ?: (any) => any,
|
|
70
|
+
allowedGroups ?: Object,
|
|
71
|
+
metadata ?: Array < Metadata >,
|
|
59
72
|
}
|
|
60
73
|
|
|
61
74
|
export const Annotator = ({
|
|
@@ -101,6 +114,18 @@ export const Annotator = ({
|
|
|
101
114
|
hideSave,
|
|
102
115
|
allowComments,
|
|
103
116
|
onImagesChange,
|
|
117
|
+
groups,
|
|
118
|
+
onGroupSelect,
|
|
119
|
+
hideHistory,
|
|
120
|
+
hideNotEditingLabel,
|
|
121
|
+
showEditor,
|
|
122
|
+
showPageSelector,
|
|
123
|
+
clsColors,
|
|
124
|
+
groupColors,
|
|
125
|
+
onRecalc,
|
|
126
|
+
onSave,
|
|
127
|
+
allowedGroups,
|
|
128
|
+
metadata
|
|
104
129
|
}: Props) => {
|
|
105
130
|
if (typeof selectedImage === "string") {
|
|
106
131
|
selectedImage = (images || []).findIndex((img) => img.src === selectedImage)
|
|
@@ -148,6 +173,9 @@ export const Annotator = ({
|
|
|
148
173
|
videoSrc,
|
|
149
174
|
keyframes,
|
|
150
175
|
}),
|
|
176
|
+
imagesUpdatedAt: null,
|
|
177
|
+
imagesSavedAt: null,
|
|
178
|
+
metadata,
|
|
151
179
|
})
|
|
152
180
|
)
|
|
153
181
|
|
|
@@ -171,23 +199,63 @@ export const Annotator = ({
|
|
|
171
199
|
})
|
|
172
200
|
})
|
|
173
201
|
|
|
202
|
+
const handleSaveClick = async (e) => {
|
|
203
|
+
e.preventDefault()
|
|
204
|
+
if (onSave) {
|
|
205
|
+
onSave()
|
|
206
|
+
dispatchToReducer({
|
|
207
|
+
type: "IMAGES_SAVED",
|
|
208
|
+
savedAt: new Date()
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const handleRecalcClick = (e) => {
|
|
214
|
+
e.preventDefault()
|
|
215
|
+
if (onRecalc) {
|
|
216
|
+
onRecalc()
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const handleMetadataChange = ({ name, value, imageIndex }) => {
|
|
221
|
+
dispatchToReducer({
|
|
222
|
+
type: "UPDATE_METADATA",
|
|
223
|
+
name,
|
|
224
|
+
value,
|
|
225
|
+
imageIndex
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// trigger this on every BBox manipulation (there is currently no way to detect adding of new box!)
|
|
174
230
|
useEffect(() => {
|
|
231
|
+
if (!state.lastAction || !["BEGIN_BOX_TRANSFORM", "CHANGE_REGION", "DELETE_REGION"].includes(state.lastAction.type)) { return }
|
|
175
232
|
if (onImagesChange) {
|
|
176
|
-
onImagesChange(
|
|
233
|
+
onImagesChange(state.images)
|
|
177
234
|
}
|
|
235
|
+
dispatchToReducer({
|
|
236
|
+
type: "IMAGES_UPDATED",
|
|
237
|
+
updatedAt: new Date()
|
|
238
|
+
})
|
|
239
|
+
}, [onImagesChange, state.images, state.lastAction])
|
|
240
|
+
|
|
241
|
+
useEffect(() => {
|
|
178
242
|
if (selectedImage === undefined) return
|
|
179
243
|
dispatchToReducer({
|
|
180
244
|
type: "SELECT_IMAGE",
|
|
181
245
|
imageIndex: selectedImage,
|
|
182
246
|
image: state.images[selectedImage],
|
|
183
247
|
})
|
|
184
|
-
|
|
248
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
249
|
+
}, [onImagesChange, selectedImage])
|
|
185
250
|
|
|
186
251
|
if (!images && !videoSrc)
|
|
187
252
|
return 'Missing required prop "images" or "videoSrc"'
|
|
188
253
|
|
|
254
|
+
|
|
255
|
+
const [recalcActive, saveActive] = state.imagesSavedAt < state.imagesUpdatedAt ? [true, true] : [false, false];
|
|
256
|
+
|
|
189
257
|
return (
|
|
190
|
-
<SettingsProvider>
|
|
258
|
+
<SettingsProvider clsColors={clsColors} groupColors={groupColors}>
|
|
191
259
|
<MainLayout
|
|
192
260
|
RegionEditLabel={RegionEditLabel}
|
|
193
261
|
alwaysShowNextButton={Boolean(onNextImage)}
|
|
@@ -203,6 +271,18 @@ export const Annotator = ({
|
|
|
203
271
|
hideSettings={hideSettings}
|
|
204
272
|
hideFullScreen={hideFullScreen}
|
|
205
273
|
hideSave={hideSave}
|
|
274
|
+
groups={groups}
|
|
275
|
+
onGroupSelect={onGroupSelect}
|
|
276
|
+
hideHistory={hideHistory}
|
|
277
|
+
hideNotEditingLabel={hideNotEditingLabel}
|
|
278
|
+
showEditor={showEditor}
|
|
279
|
+
showPageSelector={showPageSelector}
|
|
280
|
+
onRecalc={handleRecalcClick}
|
|
281
|
+
onSave={handleSaveClick}
|
|
282
|
+
saveActive={recalcActive}
|
|
283
|
+
recalcActive={saveActive}
|
|
284
|
+
allowedGroups={allowedGroups}
|
|
285
|
+
onMetadataChange={handleMetadataChange}
|
|
206
286
|
/>
|
|
207
287
|
</SettingsProvider>
|
|
208
288
|
)
|
|
@@ -12,7 +12,9 @@ import { defaultKeyMap } from "../ShortcutsManager"
|
|
|
12
12
|
|
|
13
13
|
import Annotator from "./"
|
|
14
14
|
|
|
15
|
-
import { testRegions } from "../ImageCanvas/index.story"
|
|
15
|
+
import { testRegions, testRegionsBoxes } from "../ImageCanvas/index.story"
|
|
16
|
+
import photosToImages from "../utils/photosToImages"
|
|
17
|
+
import examplePhotos from "./examplePhotos"
|
|
16
18
|
|
|
17
19
|
const middlewares = [
|
|
18
20
|
(store) => (next) => (action) => {
|
|
@@ -54,33 +56,80 @@ storiesOf("Annotator", module)
|
|
|
54
56
|
))
|
|
55
57
|
.add("Basic with onImagesChange", () => (
|
|
56
58
|
<Annotator
|
|
57
|
-
onExit={actionAddon("onExit")}
|
|
58
59
|
middlewares={middlewares}
|
|
59
60
|
labelImages
|
|
60
|
-
regionClsList={[
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{
|
|
76
|
-
src: "https://loremflickr.com/100/100/cars?lock=2",
|
|
77
|
-
name: "Frame 0037",
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
src: "https://loremflickr.com/100/100/cars?lock=3",
|
|
81
|
-
name: "Frame 0038",
|
|
82
|
-
},
|
|
61
|
+
regionClsList={[
|
|
62
|
+
"author",
|
|
63
|
+
"appendix",
|
|
64
|
+
"photo_author",
|
|
65
|
+
"photo_caption",
|
|
66
|
+
"advertisement",
|
|
67
|
+
"other_graphics",
|
|
68
|
+
"unknown",
|
|
69
|
+
"title",
|
|
70
|
+
"about_author",
|
|
71
|
+
"image",
|
|
72
|
+
"subtitle",
|
|
73
|
+
"interview",
|
|
74
|
+
"table",
|
|
75
|
+
"text",
|
|
83
76
|
]}
|
|
77
|
+
allowedGroups={[
|
|
78
|
+
{ value: '0', label: '0' },
|
|
79
|
+
{ value: '1', label: '1' },
|
|
80
|
+
{ value: '2', label: '2' },
|
|
81
|
+
{ value: '3', label: '3' },
|
|
82
|
+
{ value: '4', label: '4' },
|
|
83
|
+
{ value: '5', label: '5' },
|
|
84
|
+
{ value: '6', label: '6' },
|
|
85
|
+
{ value: '7', label: '7' },
|
|
86
|
+
{ value: '8', label: '8' },
|
|
87
|
+
{ value: '9', label: '9' },
|
|
88
|
+
]}
|
|
89
|
+
onImagesChange={(images) => console.log("[images changed to]:", images)}
|
|
90
|
+
images={photosToImages([...examplePhotos, ...examplePhotos, ...examplePhotos])}
|
|
91
|
+
clsColors={{
|
|
92
|
+
title: "#f70202",
|
|
93
|
+
subtitle: "#ffb405",
|
|
94
|
+
text: "#14deef",
|
|
95
|
+
author: "#f8d51e",
|
|
96
|
+
appendix: "#bfede2",
|
|
97
|
+
photo_author: "#9a17bb",
|
|
98
|
+
photo_caption: "#ff84f6",
|
|
99
|
+
advertisement: "#ffb201",
|
|
100
|
+
other_graphics: "#ff5400",
|
|
101
|
+
unknown: "#bfede2",
|
|
102
|
+
about_author: "#9a17bb",
|
|
103
|
+
image: "#14deef",
|
|
104
|
+
interview: "#23b20f",
|
|
105
|
+
table: "#02b4ba",
|
|
106
|
+
}}
|
|
107
|
+
groupColors={{
|
|
108
|
+
"0": "#e3a7c0",
|
|
109
|
+
"1": "#c2d5a8",
|
|
110
|
+
"2": "#f2e9cc",
|
|
111
|
+
"3": "#bad5f0",
|
|
112
|
+
"4": "#f0d5ba",
|
|
113
|
+
"5": "#d6eff5",
|
|
114
|
+
"6": "#f8d7e8",
|
|
115
|
+
"7": "#a5d5d5",
|
|
116
|
+
"8": "#b0abcb",
|
|
117
|
+
"9": "#fae4cc",
|
|
118
|
+
}}
|
|
119
|
+
onGroupSelect={(groupId) => console.log('selected groupid:', groupId)}
|
|
120
|
+
hideHeader={true}
|
|
121
|
+
hideHistory={true}
|
|
122
|
+
hideNotEditingLabel={true}
|
|
123
|
+
showEditor={true}
|
|
124
|
+
showPageSelector={true}
|
|
125
|
+
metadata={[{
|
|
126
|
+
key: "name", value: "Dennik Aha"
|
|
127
|
+
}, {
|
|
128
|
+
key: "released", value: "20/1/2022"
|
|
129
|
+
}]}
|
|
130
|
+
onSave={() => console.log("[onSave] triggered]")}
|
|
131
|
+
onRecalc={() => console.log("[onRecalc] triggered]")}
|
|
132
|
+
onExit={(s) => console.log('[onExit] triggered:', s)}
|
|
84
133
|
/>
|
|
85
134
|
))
|
|
86
135
|
.add("Basic - Allow Comments", () => (
|
|
@@ -12,6 +12,7 @@ import convertExpandingLineToPolygon from "./convert-expanding-line-to-polygon"
|
|
|
12
12
|
import clamp from "clamp"
|
|
13
13
|
import getLandmarksWithTransform from "../../utils/get-landmarks-with-transform"
|
|
14
14
|
import setInLocalStorage from "../../utils/set-in-local-storage"
|
|
15
|
+
import onlyUnique from "../../utils/filter-only-unique"
|
|
15
16
|
|
|
16
17
|
const getRandomId = () => Math.random().toString().split(".")[1]
|
|
17
18
|
|
|
@@ -167,8 +168,17 @@ export default (state: MainLayoutState, action: Action) => {
|
|
|
167
168
|
const regions = [...(activeImage.regions || [])].map((r) => ({
|
|
168
169
|
...r,
|
|
169
170
|
highlighted: r.id === region.id,
|
|
171
|
+
groupHighlighted: (r.groupId && r.groupId === region.groupId) ? true : false,
|
|
170
172
|
editingLabels: r.id === region.id,
|
|
171
173
|
}))
|
|
174
|
+
|
|
175
|
+
const selectedGroupIds = regions.filter(i => i.highlighted).map(r => r.groupId || '').filter(onlyUnique);
|
|
176
|
+
if (selectedGroupIds.length === 1) {
|
|
177
|
+
state = setIn(state, [...pathToActiveImage, "selectedGroupId"], selectedGroupIds[0])
|
|
178
|
+
}
|
|
179
|
+
if (selectedGroupIds.length === 0) {
|
|
180
|
+
state = setIn(state, [...pathToActiveImage, "selectedGroupId"], null)
|
|
181
|
+
}
|
|
172
182
|
return setIn(state, [...pathToActiveImage, "regions"], regions)
|
|
173
183
|
}
|
|
174
184
|
case "BEGIN_MOVE_POINT": {
|
|
@@ -310,15 +320,15 @@ export default (state: MainLayoutState, action: Action) => {
|
|
|
310
320
|
xFree === 0
|
|
311
321
|
? ow
|
|
312
322
|
: xFree === -1
|
|
313
|
-
|
|
314
|
-
|
|
323
|
+
? ow + (ox - dx)
|
|
324
|
+
: Math.max(0, ow + (x - ox - ow))
|
|
315
325
|
const dy = yFree === 0 ? oy : yFree === -1 ? Math.min(oy + oh, y) : oy
|
|
316
326
|
const dh =
|
|
317
327
|
yFree === 0
|
|
318
328
|
? oh
|
|
319
329
|
: yFree === -1
|
|
320
|
-
|
|
321
|
-
|
|
330
|
+
? oh + (oy - dy)
|
|
331
|
+
: Math.max(0, oh + (y - oy - oh))
|
|
322
332
|
|
|
323
333
|
// determine if we should switch the freedom
|
|
324
334
|
if (dw <= 0.001) {
|
|
@@ -636,18 +646,18 @@ export default (state: MainLayoutState, action: Action) => {
|
|
|
636
646
|
const [[keypointsDefinitionId, { landmarks, connections }]] =
|
|
637
647
|
(Object.entries(state.keypointDefinitions): any)
|
|
638
648
|
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
649
|
+
newRegion = {
|
|
650
|
+
type: "keypoints",
|
|
651
|
+
keypointsDefinitionId,
|
|
652
|
+
points: getLandmarksWithTransform({
|
|
653
|
+
landmarks,
|
|
654
|
+
center: { x, y },
|
|
655
|
+
scale: 1,
|
|
656
|
+
}),
|
|
657
|
+
highlighted: true,
|
|
658
|
+
editingLabels: false,
|
|
659
|
+
id: getRandomId(),
|
|
660
|
+
}
|
|
651
661
|
state = setIn(state, ["mode"], {
|
|
652
662
|
mode: "RESIZE_KEYPOINTS",
|
|
653
663
|
landmarks,
|
|
@@ -664,10 +674,12 @@ export default (state: MainLayoutState, action: Action) => {
|
|
|
664
674
|
|
|
665
675
|
const regions = [...(getIn(state, pathToActiveImage).regions || [])]
|
|
666
676
|
.map((r) =>
|
|
667
|
-
setIn(r, ["editingLabels"], false).setIn(["highlighted"], false)
|
|
677
|
+
setIn(r, ["editingLabels"], false).setIn(["highlighted"], false).setIn(["groupHighlighted"], false)
|
|
668
678
|
)
|
|
669
679
|
.concat(newRegion ? [newRegion] : [])
|
|
670
680
|
|
|
681
|
+
state = setIn(state, [...pathToActiveImage, "selectedGroupId"], null)
|
|
682
|
+
|
|
671
683
|
return setIn(state, [...pathToActiveImage, "regions"], regions)
|
|
672
684
|
}
|
|
673
685
|
case "MOUSE_UP": {
|
|
@@ -907,6 +919,76 @@ export default (state: MainLayoutState, action: Action) => {
|
|
|
907
919
|
}
|
|
908
920
|
break
|
|
909
921
|
}
|
|
922
|
+
case "UPDATE_REGIONS": {
|
|
923
|
+
const { imageIndex, regions: newRegions } = action;
|
|
924
|
+
const updatedRegions = state.images[imageIndex].regions.map(r => {
|
|
925
|
+
const updatedRegion = newRegions.find(i => i.id === r.id)
|
|
926
|
+
if (!updatedRegion) {
|
|
927
|
+
return r
|
|
928
|
+
}
|
|
929
|
+
return {
|
|
930
|
+
...r,
|
|
931
|
+
cls: updatedRegion.cls,
|
|
932
|
+
text: updatedRegion.text
|
|
933
|
+
}
|
|
934
|
+
})
|
|
935
|
+
// TODO: add mutation of order and deletion of regions - SI-1967
|
|
936
|
+
return setIn(
|
|
937
|
+
state,
|
|
938
|
+
["images", imageIndex, "regions"],
|
|
939
|
+
updatedRegions
|
|
940
|
+
)
|
|
941
|
+
}
|
|
942
|
+
case "IMAGES_UPDATED": {
|
|
943
|
+
const { updatedAt } = action;
|
|
944
|
+
return setIn(
|
|
945
|
+
state,
|
|
946
|
+
["imagesUpdatedAt"],
|
|
947
|
+
updatedAt
|
|
948
|
+
)
|
|
949
|
+
}
|
|
950
|
+
case "IMAGES_SAVED": {
|
|
951
|
+
const { savedAt } = action;
|
|
952
|
+
return setIn(
|
|
953
|
+
state,
|
|
954
|
+
["imagesSavedAt"],
|
|
955
|
+
savedAt
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
case "UPDATE_METADATA": {
|
|
959
|
+
const { name, value, imageIndex } = action;
|
|
960
|
+
if (isNaN(imageIndex)) {
|
|
961
|
+
// update global metadata
|
|
962
|
+
const metadataIndex = state.metadata?.findIndex(mt => mt.key === name)
|
|
963
|
+
if (metadataIndex < 0) {
|
|
964
|
+
console.error(`can't find metadata by key "${name}"`)
|
|
965
|
+
return
|
|
966
|
+
}
|
|
967
|
+
return setIn(
|
|
968
|
+
state,
|
|
969
|
+
["metadata", metadataIndex],
|
|
970
|
+
{
|
|
971
|
+
key: name,
|
|
972
|
+
value: value
|
|
973
|
+
}
|
|
974
|
+
)
|
|
975
|
+
} else {
|
|
976
|
+
// update local metadata of imageIndex
|
|
977
|
+
const metadataIndex = state.images[imageIndex]?.metadata?.findIndex(mt => mt.key === name)
|
|
978
|
+
if (metadataIndex < 0) {
|
|
979
|
+
console.error(`can't find metadata by key "${name}"`)
|
|
980
|
+
return
|
|
981
|
+
}
|
|
982
|
+
return setIn(
|
|
983
|
+
state,
|
|
984
|
+
["images", imageIndex, "metadata", metadataIndex],
|
|
985
|
+
{
|
|
986
|
+
key: name,
|
|
987
|
+
value: value
|
|
988
|
+
}
|
|
989
|
+
)
|
|
990
|
+
}
|
|
991
|
+
}
|
|
910
992
|
default:
|
|
911
993
|
break
|
|
912
994
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin styles
|
|
3
|
+
*/
|
|
4
|
+
.ce-header {
|
|
5
|
+
padding: 0.6em 0 3px;
|
|
6
|
+
margin: 0;
|
|
7
|
+
line-height: 1.25em;
|
|
8
|
+
outline: none;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.ce-header p,
|
|
12
|
+
.ce-header div {
|
|
13
|
+
padding: 0 !important;
|
|
14
|
+
margin: 0 !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Styles for Plugin icon in Toolbar
|
|
19
|
+
*/
|
|
20
|
+
.ce-header__icon {}
|
|
21
|
+
|
|
22
|
+
.ce-header[contentEditable=true][data-placeholder]::before {
|
|
23
|
+
position: absolute;
|
|
24
|
+
content: attr(data-placeholder);
|
|
25
|
+
color: #707684;
|
|
26
|
+
font-weight: normal;
|
|
27
|
+
display: none;
|
|
28
|
+
cursor: text;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.ce-header[contentEditable=true][data-placeholder]:empty::before {
|
|
32
|
+
display: block;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.ce-header[contentEditable=true][data-placeholder]:empty:focus::before {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Custom overwrite */
|
|
40
|
+
.cdx-settings-button {
|
|
41
|
+
width: 100% !important;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.ce-settings__plugin-zone {
|
|
45
|
+
padding: 0 .25rem;
|
|
46
|
+
}
|