dante2-editor 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +33 -0
- data/Gemfile +25 -0
- data/Gemfile.lock +42 -0
- data/README.md +107 -0
- data/app/assets/index.html +55 -0
- data/app/assets/license.html +52 -0
- data/app/assets/options.html +57 -0
- data/app/assets/store.json +1 -0
- data/app/components/App.cjsx +1083 -0
- data/app/components/blocks/embed.cjsx +109 -0
- data/app/components/blocks/image.cjsx +316 -0
- data/app/components/blocks/placeholder.cjsx +60 -0
- data/app/components/blocks/video.cjsx +75 -0
- data/app/components/debug.cjsx +96 -0
- data/app/components/decorators/link.cjsx +61 -0
- data/app/components/popovers/addButton.cjsx +247 -0
- data/app/components/popovers/image.cjsx +160 -0
- data/app/components/popovers/link.cjsx +87 -0
- data/app/components/popovers/toolTip.cjsx +326 -0
- data/app/data/poc.js +15 -0
- data/app/demo.js +7 -0
- data/app/images/dante/media-loading-placeholder.png +0 -0
- data/app/images/site/dante-demo.png +0 -0
- data/app/images/site/dante-editor-logo.png +0 -0
- data/app/images/site/github-logo.png +0 -0
- data/app/initialize.js +4 -0
- data/app/model/index.js +194 -0
- data/app/styles/dante.scss +22 -0
- data/app/styles/dante/_animations.scss +54 -0
- data/app/styles/dante/_caption.scss +61 -0
- data/app/styles/dante/_debug.scss +124 -0
- data/app/styles/dante/_fonts.scss +17 -0
- data/app/styles/dante/_graf.scss +242 -0
- data/app/styles/dante/_icons.scss +62 -0
- data/app/styles/dante/_media.scss +39 -0
- data/app/styles/dante/_menu.scss +201 -0
- data/app/styles/dante/_needsorder.scss +209 -0
- data/app/styles/dante/_popover.scss +212 -0
- data/app/styles/dante/_post.scss +67 -0
- data/app/styles/dante/_scaffold.scss +24 -0
- data/app/styles/dante/_tooltip.scss +130 -0
- data/app/styles/dante/_utilities.scss +59 -0
- data/app/styles/dante/_variables.scss +96 -0
- data/app/styles/dante/blame.scss +246 -0
- data/app/styles/draft.css +297 -0
- data/app/styles/fonts/dante/dante.eot +0 -0
- data/app/styles/fonts/dante/dante.svg +18 -0
- data/app/styles/fonts/dante/dante.ttf +0 -0
- data/app/styles/fonts/dante/dante.woff +0 -0
- data/app/styles/fonts/dante/fontello.eot +0 -0
- data/app/styles/fonts/dante/fontello.svg +36 -0
- data/app/styles/fonts/dante/fontello.ttf +0 -0
- data/app/styles/fonts/dante/fontello.woff +0 -0
- data/app/styles/layout/layout.scss +64 -0
- data/app/styles/layout/normalize.css +375 -0
- data/app/styles/layout/scaffold.scss +8 -0
- data/app/styles/layout/tooltips.scss +216 -0
- data/app/utils/find_entities.coffee +20 -0
- data/app/utils/html2content.coffee +120 -0
- data/app/utils/logger.coffee +0 -0
- data/app/utils/save_content.coffee +63 -0
- data/app/utils/selection.js +53 -0
- data/config.ru +64 -0
- data/dante2.gemspec +19 -0
- data/docs/app.css +2 -0
- data/docs/app.css.map +1 -0
- data/docs/app.js +3 -0
- data/docs/app.js.map +1 -0
- data/docs/dante-vendors.js +28 -0
- data/docs/dante-vendors.js.map +1 -0
- data/docs/dante.css +2 -0
- data/docs/dante.css.map +1 -0
- data/docs/dante.js +4 -0
- data/docs/dante.js.map +1 -0
- data/docs/doc.html +57 -0
- data/docs/fonts/dante.eot +0 -0
- data/docs/fonts/dante.svg +18 -0
- data/docs/fonts/dante.ttf +0 -0
- data/docs/fonts/dante.woff +0 -0
- data/docs/fonts/fontello.eot +0 -0
- data/docs/fonts/fontello.svg +36 -0
- data/docs/fonts/fontello.ttf +0 -0
- data/docs/fonts/fontello.woff +0 -0
- data/docs/images/dante-editor-logo.png +0 -0
- data/docs/images/github-logo.png +0 -0
- data/docs/index.html +55 -0
- data/docs/license.html +52 -0
- data/lib/dante2-editor.rb +5 -0
- data/lib/dante2-editor/rails.rb +16 -0
- data/lib/dante2-editor/version.rb +5 -0
- data/package.json +61 -0
- data/rakefile +1 -0
- data/webpack.config.js +148 -0
- data/yarn.lock +4704 -0
- metadata +139 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
|
2
|
+
React = require('react')
|
3
|
+
ReactDOM = require('react-dom')
|
4
|
+
|
5
|
+
{
|
6
|
+
Entity,
|
7
|
+
RichUtils
|
8
|
+
AtomicBlockUtils
|
9
|
+
EditorBlock
|
10
|
+
} = require('draft-js')
|
11
|
+
|
12
|
+
axios = require("axios")
|
13
|
+
|
14
|
+
{ updateDataOfBlock } = require('../../model/index.js')
|
15
|
+
|
16
|
+
class EmbedBlock extends React.Component
|
17
|
+
constructor: (props) ->
|
18
|
+
super props
|
19
|
+
#api_key = "86c28a410a104c8bb58848733c82f840"
|
20
|
+
|
21
|
+
@state =
|
22
|
+
embed_data: @defaultData()
|
23
|
+
error: ""
|
24
|
+
|
25
|
+
defaultData: ->
|
26
|
+
existing_data = @.props.block.getData().toJS()
|
27
|
+
existing_data.embed_data || {}
|
28
|
+
|
29
|
+
# will update block state
|
30
|
+
updateData: =>
|
31
|
+
blockProps = @.props.blockProps
|
32
|
+
block = @.props.block
|
33
|
+
getEditorState = @props.blockProps.getEditorState
|
34
|
+
setEditorState = @props.blockProps.setEditorState
|
35
|
+
data = block.getData();
|
36
|
+
newData = data.merge(@state)
|
37
|
+
setEditorState(updateDataOfBlock(getEditorState(), block, newData))
|
38
|
+
|
39
|
+
dataForUpdate: =>
|
40
|
+
|
41
|
+
@.props.blockProps.data.toJS()
|
42
|
+
|
43
|
+
componentDidMount: =>
|
44
|
+
|
45
|
+
return unless @.props.blockProps.data
|
46
|
+
|
47
|
+
# ensure data isnt already loaded
|
48
|
+
# unless @dataForUpdate().endpoint or @dataForUpdate().provisory_text
|
49
|
+
|
50
|
+
unless @dataForUpdate().endpoint or @dataForUpdate().provisory_text
|
51
|
+
#debugger
|
52
|
+
return
|
53
|
+
|
54
|
+
axios
|
55
|
+
method: 'get'
|
56
|
+
url: "#{@dataForUpdate().endpoint}#{@dataForUpdate().provisory_text}&scheme=https"
|
57
|
+
.then (result)=>
|
58
|
+
|
59
|
+
@setState
|
60
|
+
embed_data: result.data #JSON.parse(data.responseText)
|
61
|
+
, @updateData
|
62
|
+
.catch (error)=>
|
63
|
+
|
64
|
+
@setState
|
65
|
+
error: error.response.data.error_message
|
66
|
+
console.log("TODO: error")
|
67
|
+
|
68
|
+
classForImage: ->
|
69
|
+
if @state.embed_data.images then "" else "mixtapeImage--empty u-ignoreBlock"
|
70
|
+
#if @state.embed_data.thumbnail_url then "" else "mixtapeImage--empty u-ignoreBlock"
|
71
|
+
|
72
|
+
picture: ->
|
73
|
+
if @state.embed_data.images and @state.embed_data.images.length > 0 then @state.embed_data.images[0].url else ""
|
74
|
+
|
75
|
+
render: ->
|
76
|
+
#block = @.props;
|
77
|
+
#foo = @.props.blockProps;
|
78
|
+
#data = Entity.get(block.block.getEntityAt(0)).getData();
|
79
|
+
console.log "ERROR", @state.error
|
80
|
+
return(
|
81
|
+
<span>
|
82
|
+
{
|
83
|
+
if @picture()
|
84
|
+
<a target='_blank'
|
85
|
+
className="js-mixtapeImage mixtapeImage #{@classForImage()}"
|
86
|
+
href={@state.embed_data.url}
|
87
|
+
style={{backgroundImage: "url('#{@picture()}')"}}>
|
88
|
+
</a>
|
89
|
+
}
|
90
|
+
|
91
|
+
{
|
92
|
+
if @state.error
|
93
|
+
<h2>{@state.error}</h2>
|
94
|
+
}
|
95
|
+
|
96
|
+
<a className='markup--anchor markup--mixtapeEmbed-anchor'
|
97
|
+
target='_blank' href={@state.embed_data.url}>
|
98
|
+
<strong className='markup--strong markup--mixtapeEmbed-strong'>
|
99
|
+
{@state.embed_data.title}
|
100
|
+
</strong>
|
101
|
+
<em className='markup--em markup--mixtapeEmbed-em'>
|
102
|
+
{@state.embed_data.description}
|
103
|
+
</em>
|
104
|
+
</a>
|
105
|
+
{@state.embed_data.provider_url}
|
106
|
+
</span>
|
107
|
+
)
|
108
|
+
|
109
|
+
module.exports = EmbedBlock
|
@@ -0,0 +1,316 @@
|
|
1
|
+
React = require('react')
|
2
|
+
ReactDOM = require('react-dom')
|
3
|
+
|
4
|
+
{
|
5
|
+
Entity,
|
6
|
+
RichUtils
|
7
|
+
AtomicBlockUtils
|
8
|
+
EditorBlock
|
9
|
+
EditorState
|
10
|
+
} = require('draft-js')
|
11
|
+
|
12
|
+
axios = require("axios")
|
13
|
+
|
14
|
+
{ updateDataOfBlock } = require('../../model/index.js')
|
15
|
+
|
16
|
+
|
17
|
+
class ImageBlock extends React.Component
|
18
|
+
|
19
|
+
constructor: (props) ->
|
20
|
+
super props
|
21
|
+
|
22
|
+
existing_data = @.props.block.getData().toJS()
|
23
|
+
|
24
|
+
@config = @props.blockProps.config
|
25
|
+
@file = @props.blockProps.data.get('file')
|
26
|
+
@state =
|
27
|
+
loading: false
|
28
|
+
selected: false
|
29
|
+
loading_progress: 0
|
30
|
+
enabled: false
|
31
|
+
caption: @defaultPlaceholder()
|
32
|
+
direction: existing_data.direction || "center"
|
33
|
+
width: 0
|
34
|
+
height: 0
|
35
|
+
file: null
|
36
|
+
url: @blockPropsSrc() || @defaultUrl(existing_data)
|
37
|
+
aspect_ratio: @defaultAspectRatio(existing_data)
|
38
|
+
|
39
|
+
blockPropsSrc: ()=>
|
40
|
+
# console.log @.props.blockProps.data.src
|
41
|
+
@.props.blockProps.data.src
|
42
|
+
###
|
43
|
+
debugger
|
44
|
+
block = @.props;
|
45
|
+
entity = block.block.getEntityAt(0)
|
46
|
+
if entity
|
47
|
+
data = Entity.get(entity).getData().src
|
48
|
+
else
|
49
|
+
null
|
50
|
+
###
|
51
|
+
|
52
|
+
defaultUrl: (data)=>
|
53
|
+
return data.url if data.url
|
54
|
+
|
55
|
+
if data.url
|
56
|
+
if data.file then URL.createObjectURL(data.file) else data.url
|
57
|
+
else
|
58
|
+
@props.blockProps.data.src
|
59
|
+
|
60
|
+
defaultPlaceholder: ->
|
61
|
+
@props.blockProps.config.image_caption_placeholder
|
62
|
+
|
63
|
+
defaultAspectRatio: (data)=>
|
64
|
+
if data.aspect_ratio
|
65
|
+
width: data.aspect_ratio['width']
|
66
|
+
height: data.aspect_ratio['height']
|
67
|
+
ratio: data.aspect_ratio['ratio']
|
68
|
+
else
|
69
|
+
width: 0
|
70
|
+
height: 0
|
71
|
+
ratio: 100
|
72
|
+
|
73
|
+
getAspectRatio: (w, h)->
|
74
|
+
maxWidth = 1000
|
75
|
+
maxHeight = 1000
|
76
|
+
ratio = 0
|
77
|
+
width = w # Current image width
|
78
|
+
height = h # Current image height
|
79
|
+
|
80
|
+
# Check if the current width is larger than the max
|
81
|
+
if width > maxWidth
|
82
|
+
ratio = maxWidth / width # get ratio for scaling image
|
83
|
+
height = height * ratio # Reset height to match scaled image
|
84
|
+
width = width * ratio # Reset width to match scaled image
|
85
|
+
|
86
|
+
# Check if current height is larger than max
|
87
|
+
else if height > maxHeight
|
88
|
+
ratio = maxHeight / height # get ratio for scaling image
|
89
|
+
width = width * ratio # Reset width to match scaled image
|
90
|
+
height = height * ratio # Reset height to match scaled image
|
91
|
+
|
92
|
+
fill_ratio = height / width * 100
|
93
|
+
result = { width: width, height: height, ratio: fill_ratio }
|
94
|
+
# console.log result
|
95
|
+
result
|
96
|
+
|
97
|
+
# will update block state
|
98
|
+
updateData: =>
|
99
|
+
blockProps = @.props.blockProps
|
100
|
+
block = @.props.block
|
101
|
+
getEditorState = @props.blockProps.getEditorState
|
102
|
+
setEditorState = @props.blockProps.setEditorState
|
103
|
+
data = block.getData()
|
104
|
+
newData = data.merge(@state).merge(forceUpload: false)
|
105
|
+
setEditorState(updateDataOfBlock(getEditorState(), block, newData))
|
106
|
+
|
107
|
+
replaceImg: =>
|
108
|
+
@img = new Image();
|
109
|
+
@img.src = this.refs.image_tag.src
|
110
|
+
@setState
|
111
|
+
url: @img.src
|
112
|
+
self = @
|
113
|
+
# exit only when not blob and not forceUload
|
114
|
+
return if !@img.src.includes("blob:") and !@props.block.data.get("forceUpload")
|
115
|
+
@img.onload = ()=>
|
116
|
+
@setState
|
117
|
+
width: @img.width
|
118
|
+
height: @img.height
|
119
|
+
aspect_ratio: self.getAspectRatio(@img.width, @img.height)
|
120
|
+
|
121
|
+
@handleUpload()
|
122
|
+
|
123
|
+
startLoader: =>
|
124
|
+
@setState
|
125
|
+
loading: true
|
126
|
+
|
127
|
+
stopLoader: =>
|
128
|
+
@setState
|
129
|
+
loading: false
|
130
|
+
|
131
|
+
handleUpload: =>
|
132
|
+
@startLoader()
|
133
|
+
@props.blockProps.addLock()
|
134
|
+
@updateData()
|
135
|
+
@uploadFile()
|
136
|
+
|
137
|
+
componentDidMount: ->
|
138
|
+
@replaceImg()
|
139
|
+
|
140
|
+
aspectRatio: =>
|
141
|
+
maxWidth: "#{@state.aspect_ratio.width}"
|
142
|
+
maxHeight: "#{@state.aspect_ratio.height}"
|
143
|
+
ratio: "#{@state.aspect_ratio.height}"
|
144
|
+
|
145
|
+
updateDataSelection: =>
|
146
|
+
getEditorState = @props.blockProps.getEditorState
|
147
|
+
setEditorState = @props.blockProps.setEditorState
|
148
|
+
newselection = getEditorState().getSelection().merge(
|
149
|
+
anchorKey: @.props.block.getKey()
|
150
|
+
focusKey: @.props.block.getKey()
|
151
|
+
)
|
152
|
+
|
153
|
+
setEditorState(
|
154
|
+
EditorState.forceSelection(getEditorState(), newselection)
|
155
|
+
)
|
156
|
+
|
157
|
+
handleGrafFigureSelectImg: (e)=>
|
158
|
+
e.preventDefault()
|
159
|
+
@setState
|
160
|
+
selected: true
|
161
|
+
, @updateDataSelection
|
162
|
+
|
163
|
+
#main_editor.onChange(main_editor.state.editorState)
|
164
|
+
|
165
|
+
coords: ->
|
166
|
+
maxWidth: "#{@state.aspect_ratio.width}px"
|
167
|
+
maxHeight: "#{@state.aspect_ratio.height}px"
|
168
|
+
|
169
|
+
getBase64Image: (img) ->
|
170
|
+
canvas = document.createElement("canvas")
|
171
|
+
canvas.width = img.width
|
172
|
+
canvas.height = img.height
|
173
|
+
ctx = canvas.getContext("2d")
|
174
|
+
ctx.drawImage img, 0, 0
|
175
|
+
dataURL = canvas.toDataURL("image/png")
|
176
|
+
|
177
|
+
return dataURL
|
178
|
+
|
179
|
+
formatData: ->
|
180
|
+
formData = new FormData()
|
181
|
+
if @file
|
182
|
+
#file = @.props.blockProps.data.get('file')
|
183
|
+
|
184
|
+
formData.append('file', @file)
|
185
|
+
return formData
|
186
|
+
else
|
187
|
+
formData.append('url',
|
188
|
+
@.props.blockProps.data.get("url")
|
189
|
+
)
|
190
|
+
return formData
|
191
|
+
|
192
|
+
getUploadUrl: =>
|
193
|
+
url = @config.upload_url
|
194
|
+
if typeof(url) is "function" then url() else url
|
195
|
+
|
196
|
+
|
197
|
+
uploadFile: =>
|
198
|
+
# console.log "FORM DATA:" , @formatData()
|
199
|
+
axios
|
200
|
+
method: 'post'
|
201
|
+
url: @getUploadUrl()
|
202
|
+
data: @formatData()
|
203
|
+
onUploadProgress: (e)=>
|
204
|
+
@updateProgressBar(e)
|
205
|
+
.then (result)=>
|
206
|
+
@uploadCompleted(result.data)
|
207
|
+
@props.blockProps.removeLock()
|
208
|
+
@stopLoader()
|
209
|
+
@file = null
|
210
|
+
|
211
|
+
if @config.upload_callback
|
212
|
+
@config.upload_callback(response, @)
|
213
|
+
|
214
|
+
.catch (error)=>
|
215
|
+
@props.blockProps.removeLock()
|
216
|
+
@stopLoader()
|
217
|
+
|
218
|
+
console.log("ERROR: got error uploading file #{error}")
|
219
|
+
if @config.upload_error_callback
|
220
|
+
@config.upload_error_callback(error, @)
|
221
|
+
|
222
|
+
handleUp = (json_response)=>
|
223
|
+
@uploadCompleted json_response, n
|
224
|
+
|
225
|
+
uploadCompleted: (json)=>
|
226
|
+
@setState
|
227
|
+
url: json.url
|
228
|
+
, @updateData
|
229
|
+
|
230
|
+
updateProgressBar: (e)=>
|
231
|
+
complete = @state.loading_progress
|
232
|
+
if (e.lengthComputable)
|
233
|
+
complete = e.loaded / e.total * 100
|
234
|
+
complete = complete ? complete : 0
|
235
|
+
@setState
|
236
|
+
loading_progress: complete
|
237
|
+
console.log "complete: #{complete}"
|
238
|
+
|
239
|
+
placeHolderEnabled: =>
|
240
|
+
@state.enabled or @.props.block.getText()
|
241
|
+
|
242
|
+
placeholderText: =>
|
243
|
+
return "" if @placeHolderEnabled()
|
244
|
+
"Write caption for image (optional)"
|
245
|
+
|
246
|
+
handleFocus: (e)=>
|
247
|
+
# console.log "focus on placeholder"
|
248
|
+
setTimeout =>
|
249
|
+
@setState
|
250
|
+
enabled: true
|
251
|
+
, 0
|
252
|
+
|
253
|
+
render: =>
|
254
|
+
|
255
|
+
return (
|
256
|
+
<div ref="image_tag2"
|
257
|
+
suppressContentEditableWarning={true}>
|
258
|
+
<div
|
259
|
+
className="aspectRatioPlaceholder is-locked"
|
260
|
+
style={@coords()}
|
261
|
+
onClick={@handleGrafFigureSelectImg}>
|
262
|
+
<div style={{paddingBottom: "#{@state.aspect_ratio.ratio}%" }}} className='aspect-ratio-fill'>
|
263
|
+
</div>
|
264
|
+
<img src={@state.url}
|
265
|
+
ref="image_tag"
|
266
|
+
height={@state.aspect_ratio.height}
|
267
|
+
width={@state.aspect_ratio.width}
|
268
|
+
className='graf-image'
|
269
|
+
/>
|
270
|
+
<Loader
|
271
|
+
toggle={@state.loading}
|
272
|
+
progress={@state.loading_progress}
|
273
|
+
/>
|
274
|
+
</div>
|
275
|
+
<figcaption className='imageCaption'
|
276
|
+
onMouseDown={@handleFocus}>
|
277
|
+
|
278
|
+
{
|
279
|
+
if !@state.enabled
|
280
|
+
<span className="danteDefaultPlaceholder">
|
281
|
+
{@placeholderText()}
|
282
|
+
</span>
|
283
|
+
}
|
284
|
+
|
285
|
+
<EditorBlock {...@props}
|
286
|
+
editable=true
|
287
|
+
className="imageCaption"
|
288
|
+
/>
|
289
|
+
</figcaption>
|
290
|
+
</div>
|
291
|
+
)
|
292
|
+
|
293
|
+
class Loader extends React.Component
|
294
|
+
|
295
|
+
render: ->
|
296
|
+
return (
|
297
|
+
<div>
|
298
|
+
{ if @props.toggle
|
299
|
+
<div className="image-upoader-loader">
|
300
|
+
<p>
|
301
|
+
{
|
302
|
+
if @props.progress is 100
|
303
|
+
"processing image..."
|
304
|
+
else
|
305
|
+
<span>
|
306
|
+
<span>loading</span>
|
307
|
+
{Math.round @props.progress}
|
308
|
+
</span>
|
309
|
+
}
|
310
|
+
</p>
|
311
|
+
</div>
|
312
|
+
}
|
313
|
+
</div>
|
314
|
+
)
|
315
|
+
|
316
|
+
module.exports = ImageBlock
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
React = require('react')
|
3
|
+
ReactDOM = require('react-dom')
|
4
|
+
|
5
|
+
{
|
6
|
+
Entity,
|
7
|
+
RichUtils
|
8
|
+
AtomicBlockUtils
|
9
|
+
EditorBlock
|
10
|
+
} = require('draft-js')
|
11
|
+
|
12
|
+
#utils = require("../../utils/utils")
|
13
|
+
|
14
|
+
|
15
|
+
class PlaceholderBlock extends React.Component
|
16
|
+
constructor: (props) ->
|
17
|
+
super props
|
18
|
+
@state =
|
19
|
+
enabled: false
|
20
|
+
data: @.props.blockProps.data.toJS()
|
21
|
+
|
22
|
+
placeholderText: =>
|
23
|
+
return "" if @state.enabled
|
24
|
+
@.props.blockProps.data.toJS().placeholder || @placeholderFromProps() || @defaultText()
|
25
|
+
#if @.props.blockProps.data then @.props.blockProps.data.placeholder else @defaultText()
|
26
|
+
|
27
|
+
|
28
|
+
placeholderFromProps: =>
|
29
|
+
@.props.block.toJS().placeholder
|
30
|
+
|
31
|
+
defaultText: =>
|
32
|
+
"write something "
|
33
|
+
|
34
|
+
componentDidMount: ->
|
35
|
+
|
36
|
+
handleFocus: (e)=>
|
37
|
+
# console.log "focus on placeholder"
|
38
|
+
setTimeout =>
|
39
|
+
@setState
|
40
|
+
enabled: true
|
41
|
+
, 0
|
42
|
+
|
43
|
+
classForDefault: =>
|
44
|
+
if !@state.enabled then "defaultValue defaultValue--root" else ""
|
45
|
+
|
46
|
+
render: ->
|
47
|
+
return(
|
48
|
+
<span className={@classForDefault()}
|
49
|
+
onMouseDown={@handleFocus}>
|
50
|
+
|
51
|
+
{@placeholderText()}
|
52
|
+
|
53
|
+
<EditorBlock {...@props}
|
54
|
+
className="imageCaption"
|
55
|
+
placeholder="escrive alalal"
|
56
|
+
/>
|
57
|
+
</span>
|
58
|
+
)
|
59
|
+
|
60
|
+
module.exports = PlaceholderBlock
|