dante2-editor 0.2.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/.babelrc +20 -0
  3. data/.eslint +13 -0
  4. data/.gitignore +15 -0
  5. data/README.md +5 -1
  6. data/dante2.gemspec +2 -4
  7. data/{app → demo}/assets/index.html +2 -2
  8. data/{app → demo}/assets/license.html +1 -1
  9. data/{app → demo}/assets/options.html +1 -5
  10. data/{app → demo}/assets/store.json +0 -0
  11. data/demo/data/poc.js +9 -0
  12. data/demo/demo.js +9 -0
  13. data/demo/initialize.js +7 -0
  14. data/{app → demo}/styles/layout/layout.scss +0 -0
  15. data/{app → demo}/styles/layout/normalize.css +0 -0
  16. data/{app → demo}/styles/layout/scaffold.scss +0 -0
  17. data/{app → demo}/styles/layout/tooltips.scss +0 -0
  18. data/dist/Dante2.js +2995 -0
  19. data/dist/Dante2.min.js +3 -0
  20. data/dist/Dante2.min.js.map +1 -0
  21. data/dist/DanteStyles.css +2 -0
  22. data/dist/DanteStyles.css.map +1 -0
  23. data/dist/DanteStyles.js +102 -0
  24. data/dist/DanteStyles.min.js +2 -0
  25. data/dist/DanteStyles.min.js.map +1 -0
  26. data/dist/dante-vendors.js +48316 -0
  27. data/dist/dante-vendors.min.js +29 -0
  28. data/dist/dante-vendors.min.js.map +1 -0
  29. data/{app/styles/fonts/dante → dist/fonts}/dante.eot +0 -0
  30. data/{app/styles/fonts/dante → dist/fonts}/dante.svg +0 -0
  31. data/{app/styles/fonts/dante → dist/fonts}/dante.ttf +0 -0
  32. data/{app/styles/fonts/dante → dist/fonts}/dante.woff +0 -0
  33. data/{app/styles/fonts/dante → dist/fonts}/fontello.eot +0 -0
  34. data/{app/styles/fonts/dante → dist/fonts}/fontello.svg +0 -0
  35. data/{app/styles/fonts/dante → dist/fonts}/fontello.ttf +0 -0
  36. data/{app/styles/fonts/dante → dist/fonts}/fontello.woff +0 -0
  37. data/docs/app.css +430 -1
  38. data/docs/app.css.map +1 -1
  39. data/docs/app.js +62 -2
  40. data/docs/app.js.map +1 -1
  41. data/docs/dante-vendors.js +49513 -22
  42. data/docs/dante-vendors.js.map +1 -1
  43. data/docs/dante.css +1602 -1
  44. data/docs/dante.css.map +1 -1
  45. data/docs/dante.js +3310 -3
  46. data/docs/dante.js.map +1 -1
  47. data/docs/doc.html +3 -7
  48. data/docs/index.html +4 -4
  49. data/docs/license.html +3 -3
  50. data/docs/options.html +53 -0
  51. data/package.json +47 -17
  52. data/rb_lib/dante2-editor/rails.rb +15 -0
  53. data/{lib → rb_lib}/dante2-editor/version.rb +1 -1
  54. data/{lib → rb_lib}/dante2-editor.rb +0 -0
  55. data/src/components/blocks/embed.js +126 -0
  56. data/src/components/blocks/image.js +377 -0
  57. data/src/components/blocks/placeholder.js +68 -0
  58. data/src/components/blocks/video.js +80 -0
  59. data/src/components/dante.js +291 -0
  60. data/src/components/dante_editor.js +928 -0
  61. data/src/components/debug.js +104 -0
  62. data/src/components/decorators/link.js +64 -0
  63. data/src/components/popovers/addButton.js +318 -0
  64. data/src/components/popovers/image.js +188 -0
  65. data/src/components/popovers/link.js +107 -0
  66. data/src/components/popovers/toolTip.js +402 -0
  67. data/{app/utils/logger.coffee → src/components/wrapper.js} +0 -0
  68. data/{app → src}/images/dante/media-loading-placeholder.png +0 -0
  69. data/{app → src}/images/site/dante-demo.png +0 -0
  70. data/{app → src}/images/site/dante-editor-logo.png +0 -0
  71. data/{app → src}/images/site/github-logo.png +0 -0
  72. data/src/index.js +9 -0
  73. data/{app → src}/model/index.js +0 -0
  74. data/src/style.js +3 -0
  75. data/{app → src}/styles/dante/_animations.scss +0 -0
  76. data/{app → src}/styles/dante/_caption.scss +0 -0
  77. data/{app → src}/styles/dante/_debug.scss +0 -0
  78. data/{app → src}/styles/dante/_fonts.scss +0 -0
  79. data/{app → src}/styles/dante/_graf.scss +0 -0
  80. data/{app → src}/styles/dante/_icons.scss +0 -0
  81. data/{app → src}/styles/dante/_media.scss +0 -0
  82. data/{app → src}/styles/dante/_menu.scss +0 -0
  83. data/{app → src}/styles/dante/_needsorder.scss +0 -0
  84. data/{app → src}/styles/dante/_popover.scss +0 -0
  85. data/{app → src}/styles/dante/_post.scss +0 -0
  86. data/{app → src}/styles/dante/_scaffold.scss +0 -0
  87. data/{app → src}/styles/dante/_tooltip.scss +0 -0
  88. data/{app → src}/styles/dante/_utilities.scss +0 -0
  89. data/{app → src}/styles/dante/_variables.scss +0 -0
  90. data/{app → src}/styles/dante/blame.scss +0 -0
  91. data/{app → src}/styles/dante.scss +0 -0
  92. data/{app → src}/styles/draft.css +0 -0
  93. data/src/styles/fonts/dante/dante.eot +0 -0
  94. data/src/styles/fonts/dante/dante.svg +18 -0
  95. data/src/styles/fonts/dante/dante.ttf +0 -0
  96. data/src/styles/fonts/dante/dante.woff +0 -0
  97. data/src/styles/fonts/dante/fontello.eot +0 -0
  98. data/src/styles/fonts/dante/fontello.svg +36 -0
  99. data/src/styles/fonts/dante/fontello.ttf +0 -0
  100. data/src/styles/fonts/dante/fontello.woff +0 -0
  101. data/src/utils/find_entities.js +17 -0
  102. data/src/utils/html2content.js +127 -0
  103. data/src/utils/save_content.js +80 -0
  104. data/{app → src}/utils/selection.js +0 -0
  105. data/tools/.eslintrc +9 -0
  106. data/tools/amd/README.md +7 -0
  107. data/tools/amd/bower.json +19 -0
  108. data/tools/amd/build.js +36 -0
  109. data/tools/build-cli.js +46 -0
  110. data/tools/build.js +30 -0
  111. data/tools/buildBabel.js +32 -0
  112. data/tools/constants.js +10 -0
  113. data/tools/dist/build.js +13 -0
  114. data/tools/docs/build.js +12 -0
  115. data/tools/es/build.js +27 -0
  116. data/tools/exec.js +50 -0
  117. data/tools/fs-utils.js +35 -0
  118. data/tools/lib/build.js +14 -0
  119. data/tools/promisify.js +14 -0
  120. data/webpack/base.config.js +89 -0
  121. data/webpack/docs.config.js +147 -0
  122. data/webpack/webpack.config.js +78 -0
  123. data/webpack.config-doc.js +5 -0
  124. data/webpack.config.js +3 -147
  125. data/yarn.lock +973 -718
  126. metadata +108 -64
  127. data/app/components/App.cjsx +0 -1083
  128. data/app/components/blocks/embed.cjsx +0 -109
  129. data/app/components/blocks/image.cjsx +0 -316
  130. data/app/components/blocks/placeholder.cjsx +0 -60
  131. data/app/components/blocks/video.cjsx +0 -75
  132. data/app/components/debug.cjsx +0 -96
  133. data/app/components/decorators/link.cjsx +0 -61
  134. data/app/components/popovers/addButton.cjsx +0 -247
  135. data/app/components/popovers/image.cjsx +0 -160
  136. data/app/components/popovers/link.cjsx +0 -87
  137. data/app/components/popovers/toolTip.cjsx +0 -326
  138. data/app/data/poc.js +0 -15
  139. data/app/demo.js +0 -7
  140. data/app/initialize.js +0 -4
  141. data/app/utils/find_entities.coffee +0 -20
  142. data/app/utils/html2content.coffee +0 -120
  143. data/app/utils/save_content.coffee +0 -63
  144. data/lib/dante2-editor/rails.rb +0 -16
@@ -0,0 +1,104 @@
1
+ import React from 'react'
2
+
3
+ class Debug extends React.Component {
4
+
5
+ constructor() {
6
+ super()
7
+
8
+ this.handleToggleReadOnly = this.handleToggleReadOnly.bind(this)
9
+ this.handleTestEmitAndDecode = this.handleTestEmitAndDecode.bind(this)
10
+ this.handleTestEmitTEXT = this.handleTestEmitTEXT.bind(this)
11
+ this.testEmitAndDecode = this.testEmitAndDecode.bind(this)
12
+ this.testEmitTEXT = this.testEmitTEXT.bind(this)
13
+ this.logState = this.logState.bind(this)
14
+ this.toggleDisplay = this.toggleDisplay.bind(this)
15
+ this.open = this.open.bind(this)
16
+ this.render = this.render.bind(this)
17
+ this.state = {
18
+ output: "",
19
+ display: "none"
20
+ }
21
+ }
22
+
23
+ handleToggleReadOnly(e) {
24
+ e.preventDefault()
25
+ this.props.editor.toggleEditable()
26
+ return false
27
+ }
28
+
29
+ handleTestEmitAndDecode(e) {
30
+ e.preventDefault()
31
+ return this.testEmitAndDecode()
32
+ }
33
+
34
+ handleTestEmitTEXT(e) {
35
+ e.preventDefault()
36
+ return this.testEmitTEXT()
37
+ }
38
+
39
+ testEmitAndDecode(e) {
40
+ const raw_as_json = this.props.editor.emitSerializedOutput()
41
+ this.props.editor.setState({
42
+ editorState: this.props.editor.decodeEditorContent(raw_as_json) },
43
+ this.logState(JSON.stringify(raw_as_json)))
44
+ return false
45
+ }
46
+
47
+ testEmitTEXT() {
48
+ const text = this.props.editor.getTextFromEditor()
49
+ return this.logState(text)
50
+ }
51
+
52
+ logState(raw) {
53
+ return this.setState({ output: raw }, this.open)
54
+ }
55
+
56
+ toggleDisplay(e) {
57
+ e.preventDefault()
58
+ const d = this.state.display === "block" ? "none" : this.state.display
59
+ return this.setState({
60
+ display: d })
61
+ }
62
+
63
+ open() {
64
+ return this.setState({
65
+ display: "block" })
66
+ }
67
+
68
+ render() {
69
+ return (
70
+ <div>
71
+ <div className="debugControls">
72
+ <ul>
73
+ <li> LOCKS: { this.props.editor.state.locks } </li>
74
+ <li>
75
+ <a href="#" onClick={ this.handleToggleReadOnly }>
76
+ EDITABLE: { this.props.editor.state.read_only ? 'NO' : 'YES' }
77
+ </a>
78
+ </li>
79
+ <li>
80
+ <a href="#" onClick={ this.handleTestEmitTEXT }>EDITOR TEXT</a>
81
+ </li>
82
+ <li>
83
+ <a href="#" onClick={ this.handleTestEmitAndDecode }>EDITOR STATE</a>
84
+ </li>
85
+ </ul>
86
+ </div>
87
+ <div className="debugZone" style={ { display: this.state.display } }>
88
+ <a href="#" className="dante-debug-close close" onClick={ this.toggleDisplay } />
89
+ <div className="debugOutput">
90
+ <h2>EDITOR OUTPUT</h2>
91
+ {
92
+ this.state.output.length > 0
93
+ ? <pre>{ this.state.output }</pre>
94
+ : undefined
95
+ }
96
+ </div>
97
+ </div>
98
+ </div>
99
+ )
100
+ }
101
+ }
102
+
103
+ export default Debug
104
+
@@ -0,0 +1,64 @@
1
+ import React from 'react'
2
+ import { Entity } from 'draft-js'
3
+
4
+ export default class Link extends React.Component {
5
+
6
+ constructor(props) {
7
+ super(props)
8
+ this._validateLink = this._validateLink.bind(this)
9
+ this._checkProtocol = this._checkProtocol.bind(this)
10
+ this._showPopLinkOver = this._showPopLinkOver.bind(this)
11
+ this._hidePopLinkOver = this._hidePopLinkOver.bind(this)
12
+ this.isHover = false
13
+ }
14
+
15
+ _validateLink() {
16
+ const pattern = new RegExp('^(https?:\/\/)?' + // protocol
17
+ '((([a-z\d]([a-z\d-]*[a-z\d])*)\.)+[a-z]{2,}|' + // domain name
18
+ '((\d{1,3}\.){3}\d{1,3}))' + // OR ip (v4) address
19
+ '(\:\d+)?(\/[-a-z\d%_.~+]*)*' + // port and path
20
+ '(\?[&a-z\d%_.~+=-]*)?' + // query string
21
+ '(\#[-a-z\d_]*)?$', 'i') // fragment locater
22
+ if (!pattern.test(str)) {
23
+ alert("Please enter a valid URL.")
24
+ return false
25
+ } else {
26
+ return true
27
+ }
28
+ }
29
+
30
+ _checkProtocol() {
31
+ return console.log("xcvd")
32
+ }
33
+
34
+ _showPopLinkOver(e) {
35
+ if (!this.data.showPopLinkOver) {
36
+ return
37
+ }
38
+ return this.data.showPopLinkOver(this.refs.link)
39
+ }
40
+
41
+ _hidePopLinkOver(e) {
42
+ if (!this.data.hidePopLinkOver) {
43
+ return
44
+ }
45
+ return this.data.hidePopLinkOver()
46
+ }
47
+
48
+ render() {
49
+ this.data = Entity.get(this.props.entityKey).getData()
50
+
51
+ return (
52
+ <a
53
+ ref="link"
54
+ href={ this.data.url }
55
+ className="markup--anchor"
56
+ onMouseOver={ this._showPopLinkOver }
57
+ onMouseOut={ this._hidePopLinkOver }
58
+ >
59
+ { this.props.children }
60
+ </a>
61
+ )
62
+ }
63
+ }
64
+
@@ -0,0 +1,318 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom'
3
+
4
+ import {
5
+ Entity,
6
+ RichUtils,
7
+ AtomicBlockUtils,
8
+ EditorState
9
+ } from 'draft-js'
10
+
11
+ import {
12
+ addNewBlock,
13
+ resetBlockWithType,
14
+ updateDataOfBlock,
15
+ getCurrentBlock,
16
+ getNode } from '../../model/index.js'
17
+
18
+ import { getSelectionRect, getSelection } from "../../utils/selection.js"
19
+
20
+ class DanteInlineTooltip extends React.Component {
21
+
22
+ constructor(props) {
23
+ super(props)
24
+
25
+ this.display = this.display.bind(this)
26
+ this.show = this.show.bind(this)
27
+ this.hide = this.hide.bind(this)
28
+ this._toggleScaled = this._toggleScaled.bind(this)
29
+ this.scale = this.scale.bind(this)
30
+ this.collapse = this.collapse.bind(this)
31
+ this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this)
32
+ this.clickOnFileUpload = this.clickOnFileUpload.bind(this)
33
+ this.handlePlaceholder = this.handlePlaceholder.bind(this)
34
+ this.insertImage = this.insertImage.bind(this)
35
+ this.handleFileInput = this.handleFileInput.bind(this)
36
+ this.widgets = this.widgets.bind(this)
37
+ this.clickHandler = this.clickHandler.bind(this)
38
+ this.relocate = this.relocate.bind(this)
39
+ this.state = {
40
+ position: { top: 0, left: 0 },
41
+ show: false,
42
+ scaled: false
43
+ }
44
+ }
45
+
46
+ display(b) {
47
+ if (b) {
48
+ return this.show()
49
+ } else {
50
+ return this.hide()
51
+ }
52
+ }
53
+
54
+ show() {
55
+ return this.setState({
56
+ show: true })
57
+ }
58
+
59
+ hide() {
60
+ return this.setState({
61
+ show: false })
62
+ }
63
+
64
+ setPosition(coords) {
65
+ return this.setState({
66
+ position: coords })
67
+ }
68
+
69
+ _toggleScaled(ev) {
70
+ if (this.state.scaled) {
71
+ return this.collapse()
72
+ } else {
73
+ return this.scale()
74
+ }
75
+ }
76
+
77
+ scale() {
78
+ return this.setState({
79
+ scaled: true })
80
+ }
81
+
82
+ collapse() {
83
+ return this.setState({
84
+ scaled: false })
85
+ }
86
+
87
+ componentWillReceiveProps(newProps) {
88
+ return this.collapse()
89
+ }
90
+
91
+ activeClass() {
92
+ //if @props.show then "is-active" else ""
93
+ if (this.isActive()) {
94
+ return "is-active"
95
+ } else {
96
+ return ""
97
+ }
98
+ }
99
+
100
+ isActive() {
101
+ return this.state.show
102
+ }
103
+
104
+ scaledClass() {
105
+ if (this.state.scaled) {
106
+ return "is-scaled"
107
+ } else {
108
+ return ""
109
+ }
110
+ }
111
+
112
+ scaledWidth() {
113
+ if (this.state.scaled) {
114
+ return "124"
115
+ } else {
116
+ return "0"
117
+ }
118
+ }
119
+
120
+ clickOnFileUpload() {
121
+ this.refs.fileInput.click()
122
+ this.collapse()
123
+ return this.hide()
124
+ }
125
+
126
+ handlePlaceholder(input) {
127
+ let opts = {
128
+ type: input.widget_options.insert_block,
129
+ placeholder: input.options.placeholder,
130
+ endpoint: input.options.endpoint
131
+ }
132
+
133
+ return this.props.onChange(resetBlockWithType(this.props.editorState, 'placeholder', opts))
134
+ }
135
+
136
+ insertImage(file) {
137
+ let opts = {
138
+ url: URL.createObjectURL(file),
139
+ file
140
+ }
141
+
142
+ return this.props.onChange(addNewBlock(this.props.editorState, 'image', opts))
143
+ }
144
+
145
+ handleFileInput(e) {
146
+ let fileList = e.target.files
147
+ // TODO: support multiple file uploads
148
+ /*
149
+ Object.keys(fileList).forEach (o)=>
150
+ @.insertImage(fileList[0])
151
+ */
152
+ return this.insertImage(fileList[0])
153
+ }
154
+
155
+ widgets() {
156
+ return this.props.editor.widgets
157
+ }
158
+
159
+ clickHandler(e, type) {
160
+ let request_block = this.widgets().find(o => o.icon === type)
161
+
162
+ switch (request_block.widget_options.insertion) {
163
+ case "upload":
164
+ return this.clickOnFileUpload(e, request_block)
165
+ case "placeholder":
166
+ return this.handlePlaceholder(request_block)
167
+ default:
168
+ return console.log(`WRONG TYPE FOR ${ request_block.widget_options.insertion }`)
169
+ }
170
+ }
171
+
172
+ getItems() {
173
+ return this.widgets().filter(o => {
174
+ return o.widget_options.displayOnInlineTooltip
175
+ })
176
+ }
177
+
178
+ isDescendant(parent, child) {
179
+ let node = child.parentNode
180
+ while (node !== null) {
181
+ if (node === parent) {
182
+ return true
183
+ }
184
+ node = node.parentNode
185
+ }
186
+ return false
187
+ }
188
+
189
+ relocate() {
190
+ let { editorState } = this.props
191
+
192
+ if (editorState.getSelection().isCollapsed()) {
193
+
194
+ let currentBlock = getCurrentBlock(editorState)
195
+ let blockType = currentBlock.getType()
196
+
197
+ let contentState = editorState.getCurrentContent()
198
+ let selectionState = editorState.getSelection()
199
+
200
+ let block = contentState.getBlockForKey(selectionState.anchorKey)
201
+
202
+ let nativeSelection = getSelection(window)
203
+ if (!nativeSelection.rangeCount) {
204
+ return
205
+ }
206
+
207
+ let node = getNode()
208
+
209
+ let selectionBoundary = getSelectionRect(nativeSelection)
210
+ let coords = selectionBoundary //utils.getSelectionDimensions(node)
211
+
212
+ let parent = ReactDOM.findDOMNode(this.props.editor)
213
+ let parentBoundary = parent.getBoundingClientRect()
214
+
215
+ // hide if selected node is not in editor
216
+ // debugger
217
+ //console.log @isDescendant(parent, nativeSelection.anchorNode)
218
+
219
+ if (!this.isDescendant(parent, nativeSelection.anchorNode)) {
220
+ this.hide()
221
+ return
222
+ }
223
+
224
+ // checkeamos si esta vacio
225
+ this.display(block.getText().length === 0 && blockType === "unstyled")
226
+ return this.setPosition({
227
+ top: coords.top + window.scrollY,
228
+ left: coords.left + window.scrollX - 60
229
+ })
230
+
231
+ /*
232
+ @refs.image_popover.display(blockType is "image")
233
+ if blockType is "image"
234
+ selectionBoundary = node.anchorNode.parentNode.parentNode.parentNode.getBoundingClientRect()
235
+ *el = document.querySelector("#dante_image_popover")
236
+ el = @refs.image_popover.refs.image_popover
237
+ padd = el.offsetWidth / 2
238
+ @refs.image_popover.setPosition
239
+ top: selectionBoundary.top - parentBoundary.top + 60
240
+ left: selectionBoundary.left + (selectionBoundary.width / 2) - padd
241
+
242
+ *@setState
243
+ * image_popover_position:
244
+ * top: selectionBoundary.top - parentBoundary.top + 60
245
+ * left: selectionBoundary.left + (selectionBoundary.width / 2) - padd
246
+ *
247
+ */
248
+ } else {
249
+ return this.hide()
250
+ }
251
+ }
252
+
253
+ render() {
254
+ return (
255
+ <div
256
+ className={ `inlineTooltip ${ this.activeClass() } ${ this.scaledClass() }` }
257
+ style={ this.state.position }
258
+ >
259
+ <button
260
+ className="inlineTooltip-button control"
261
+ title="Close Menu"
262
+ data-action="inline-menu"
263
+ onClick={ this._toggleScaled }
264
+ >
265
+ <span className="tooltip-icon dante-icon-plus" />
266
+ </button>
267
+ <div
268
+ className="inlineTooltip-menu"
269
+ style={ { width: `${ this.scaledWidth() }px` } }
270
+ >
271
+ { this.getItems().map( (item, i) => {
272
+ return <InlineTooltipItem
273
+ item={ item }
274
+ key={ i }
275
+ clickHandler={ this.clickHandler }
276
+ />
277
+ })
278
+ }
279
+ <input
280
+ type="file"
281
+ style={ { display: 'none' } }
282
+ ref="fileInput"
283
+ multiple="multiple"
284
+ onChange={ this.handleFileInput }
285
+ />
286
+ </div>
287
+ </div>
288
+ )
289
+ }
290
+ }
291
+
292
+ class InlineTooltipItem extends React.Component {
293
+
294
+ constructor(...args) {
295
+ super(...args)
296
+ this.clickHandler = this.clickHandler.bind(this)
297
+ }
298
+
299
+ clickHandler(e) {
300
+ e.preventDefault()
301
+ return this.props.clickHandler(e, this.props.item.icon)
302
+ }
303
+
304
+ render() {
305
+ return (
306
+ <button
307
+ className="inlineTooltip-button scale"
308
+ title={ this.props.title }
309
+ onMouseDown={ this.clickHandler }
310
+ >
311
+ <span className={ `tooltip-icon dante-icon-${ this.props.item.icon }` } />
312
+ </button>
313
+ )
314
+ }
315
+ }
316
+
317
+ export default DanteInlineTooltip
318
+
@@ -0,0 +1,188 @@
1
+
2
+ import React from 'react'
3
+ import ReactDOM from 'react-dom'
4
+
5
+ import { Entity, RichUtils, AtomicBlockUtils, EditorState } from 'draft-js'
6
+
7
+ import { getSelectionRect, getSelection } from "../../utils/selection.js"
8
+
9
+ import { getCurrentBlock, getNode } from '../../model/index.js'
10
+
11
+ class DanteImagePopover extends React.Component {
12
+
13
+ constructor(props) {
14
+ super(props)
15
+
16
+ this.display = this.display.bind(this)
17
+ this.show = this.show.bind(this)
18
+ this.hide = this.hide.bind(this)
19
+ this._toggleScaled = this._toggleScaled.bind(this)
20
+ this.scale = this.scale.bind(this)
21
+ this.collapse = this.collapse.bind(this)
22
+ this.relocate = this.relocate.bind(this)
23
+ this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this)
24
+ this.handleClick = this.handleClick.bind(this)
25
+ this.state = {
26
+ position: {
27
+ top: 0,
28
+ left: 0
29
+ },
30
+ show: false,
31
+ scaled: false,
32
+ buttons: [{ type: "left" },
33
+ { type: "center"},
34
+ { type: "fill" },
35
+ { type: "wide" }]
36
+ }
37
+ }
38
+
39
+ display(b) {
40
+ if (b) {
41
+ return this.show()
42
+ } else {
43
+ return this.hide()
44
+ }
45
+ }
46
+
47
+ show() {
48
+ return this.setState({
49
+ show: true })
50
+ }
51
+
52
+ hide() {
53
+ return this.setState({
54
+ show: false })
55
+ }
56
+
57
+ setPosition(coords) {
58
+ return this.setState({
59
+ position: coords })
60
+ }
61
+
62
+ _toggleScaled(ev) {
63
+ if (this.state.scaled) {
64
+ return this.collapse()
65
+ } else {
66
+ return this.scale()
67
+ }
68
+ }
69
+
70
+ scale() {
71
+ return this.setState({
72
+ scaled: true })
73
+ }
74
+
75
+ collapse() {
76
+ return this.setState({
77
+ scaled: false })
78
+ }
79
+
80
+ relocate() {
81
+ let { editorState } = this.props
82
+
83
+ if (editorState.getSelection().isCollapsed()) {
84
+
85
+ let currentBlock = getCurrentBlock(editorState)
86
+ let blockType = currentBlock.getType()
87
+
88
+ let contentState = editorState.getCurrentContent()
89
+ let selectionState = editorState.getSelection()
90
+
91
+ let block = contentState.getBlockForKey(selectionState.anchorKey)
92
+
93
+ let nativeSelection = getSelection(window)
94
+ if (!nativeSelection.rangeCount) {
95
+ return
96
+ }
97
+
98
+ let node = getNode()
99
+
100
+ let selectionBoundary = getSelectionRect(nativeSelection)
101
+ let coords = selectionBoundary
102
+
103
+ let parent = ReactDOM.findDOMNode(this.props.editor)
104
+ let parentBoundary = parent.getBoundingClientRect()
105
+
106
+ this.display(blockType === "image")
107
+
108
+ if (blockType === "image") {
109
+ selectionBoundary = node.anchorNode.parentNode.parentNode
110
+ .parentNode.getBoundingClientRect()
111
+ let el = this.refs.image_popover
112
+ let padd = el.offsetWidth / 2
113
+ return this.setPosition({
114
+ top: selectionBoundary.top - parentBoundary.top + 60,
115
+ left: selectionBoundary.left + selectionBoundary.width / 2 - padd
116
+ })
117
+ }
118
+ } else {
119
+ return this.hide()
120
+ }
121
+ }
122
+
123
+ componentWillReceiveProps(newProps) {
124
+ return this.collapse()
125
+ }
126
+
127
+ getStyle() {
128
+ if (!this.state.position) {
129
+ return {}
130
+ }
131
+ }
132
+
133
+ handleClick(item) {
134
+ return this.props.editor.setDirection(item.type)
135
+ }
136
+
137
+ render() {
138
+ return (
139
+ <div
140
+ ref="image_popover"
141
+ className={ `dante-popover popover--Aligntooltip popover--top popover--animated ${ this.state.show ? 'is-active' : undefined }` }
142
+ style={
143
+ { top: this.state.position.top,
144
+ left: this.state.position.left }
145
+ }
146
+ >
147
+ <div className='popover-inner'>
148
+ <ul className='dante-menu-buttons'>
149
+ { this.state.buttons.map( (item, i) => {
150
+ return <DanteImagePopoverItem
151
+ item={ item }
152
+ handleClick={ this.handleClick }
153
+ key={ i }
154
+ />
155
+ })
156
+ }
157
+ </ul>
158
+ </div>
159
+ <div className='popover-arrow' />
160
+ </div>
161
+ )
162
+ }
163
+ }
164
+
165
+ class DanteImagePopoverItem extends React.Component {
166
+
167
+ constructor(...args) {
168
+ super(...args)
169
+ this.handleClick = this.handleClick.bind(this)
170
+ this.render = this.render.bind(this)
171
+ }
172
+
173
+ handleClick(e) {
174
+ e.preventDefault()
175
+ return this.props.handleClick(this.props.item)
176
+ }
177
+
178
+ render() {
179
+ return <li
180
+ className={`dante-menu-button align-${ this.props.item.type }`}
181
+ onMouseDown={this.handleClick}>
182
+ <span className={`tooltip-icon dante-icon-image-${ this.props.item.type }`} />
183
+ </li>
184
+ }
185
+ }
186
+
187
+ export default DanteImagePopover
188
+