-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Drag'n'drop support #445
Drag'n'drop support #445
Changes from 250 commits
2e6b107
ef6f2d5
0ed9238
c7a5be8
8adc54c
4575a24
cf4a126
8f4b7e5
6c75a45
6d83636
cdf2ca7
6db88c3
6fd28f3
d2d85ee
4f8d089
1a9167f
ef24b23
be575a1
9dd05d2
87c94d7
342705b
cbfcecd
52ed658
d46e823
97af4c8
6a4de5f
3ae2f9b
bbc1d0d
df75c79
45dabb7
0869a64
543b73a
0c4c33e
92a316e
909080a
b4d9485
740349c
05b7663
2cc8578
fe5c99f
25f5b1d
a4099ca
c73ce70
17acac3
f1e95c3
fb07764
2e5894a
9347e44
feaf00a
f9a7c0c
c4bdc23
e6c3fc7
eded07b
d29b3ae
7c7f978
d2470fe
0f0de88
e3be9ea
3ebf499
b61fac8
550e3ce
0d8257e
e2041c9
c92f3f9
d969f7d
01a1b96
fb3e6fd
b4220c8
58d640a
12e1d37
d5b0ebe
e1028fa
4636c7a
e85725f
768348f
fc44297
24a7b9c
beeecba
5c08eda
f8043ff
3ac9ee9
1f30fa2
373579c
9c303ed
51a5ffe
d8747e5
dbb4cd6
cba999a
cd6edef
7945bce
57ee838
36f505c
0cddf41
32aa383
93ddb25
36bc3e4
59347e3
022023f
ca5beae
543d35f
2573fd6
5526648
08a6549
4413764
cac6bac
84aa54b
9d59e9c
c85f9d0
110603a
572f56f
295e425
1b5260e
4efaf0d
242b9b5
ad78d12
60eda57
c665677
9c41fc4
972eb87
779bf5d
d0126e0
088a537
c4f49d0
65397b7
fa492e5
430277e
a8c5fea
5ba9b80
4ab74f3
f72626c
0197397
b3723d0
8ef971a
773ef46
ff74576
97f04e6
a95e236
e17ccbf
319cabb
2428849
f898492
f1febf7
0c918b5
4f46afc
8112e48
d7d59bf
4b61ea5
a5c682a
1bbb136
69b70cc
e21c645
73f5de3
782a600
5222fe3
903093b
72c2240
832f0ca
640e44b
1853cfa
3d03461
9c961bd
ea64562
8696b9c
f951ced
3398383
cbb4746
dc7afd1
29d2bae
4b1d933
bc3f193
7a67d38
f2ef201
835e698
f45521d
83655d3
4346b4b
9cc108e
877e2de
c844b55
d3a7af9
6571589
2443672
9027974
11c9746
df3fd63
07eb1b7
dfb35da
1533af4
e190d67
c7549a9
1775e3c
46bc5db
3ba88fa
b8db945
ea881f0
9ca4e70
d9b0e5e
1e3551a
3fca65f
faac5ab
657fe98
d0f7e3e
61adb11
e854008
dbe862c
545ce14
7d30b04
605154c
ef2d25e
5206e03
43ffd4d
c349c87
b593dc6
6850019
9d1a33b
e88d1d3
3a72af8
e1ad086
672f7ae
a58c8d2
7b73a15
5f63cbd
7517620
d7b9c47
883ffd9
a4ef357
bfb6308
5dccbc9
570710d
b291625
a5cf1bf
e00764d
1b86c8a
f03e8ec
6a58ac7
7e9283b
ecfc5e9
3a53db2
b74045d
dac1eb4
2f9dbe8
b20a767
f7c066c
f83988e
d83c18f
a9247b8
bf6c30d
d0068ff
8ee5fff
bf694e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
import _ from '../utils'; | ||
import IBlockToolData from '../interfaces/tools/block-tool-data'; | ||
import SelectionUtils from '../selection'; | ||
|
||
declare var Module: any; | ||
|
||
interface DropConfig { | ||
extensions: string; | ||
mimeTypes: string; | ||
handler: (file: File) => IBlockToolData; | ||
} | ||
|
||
export default class DragNDrop extends Module { | ||
|
||
/** | ||
* If drag has been started at editor, we save it | ||
* | ||
* @type Boolean | ||
* @private | ||
*/ | ||
private isStartedAtEditor = false; | ||
|
||
/** | ||
* Cache for Tools onDrop configs | ||
* | ||
* @private | ||
*/ | ||
private toolsDropConfigs: {[tool: string]: { | ||
extensions: string[], | ||
mimeTypes: string[], | ||
handler: (file: File) => IBlockToolData, | ||
}} = {}; | ||
|
||
/** | ||
* Bind events and process tools configs | ||
* | ||
* @private | ||
*/ | ||
public async prepare(): Promise<void> { | ||
this.bindEvents(); | ||
await this.processTools(); | ||
} | ||
|
||
/** | ||
* Add drag events listeners to editor zone | ||
* @private | ||
*/ | ||
private bindEvents(): void { | ||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'drop', this.processDrop, true); | ||
|
||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragstart', () => { | ||
this.isStartedAtEditor = true; | ||
this.Editor.InlineToolbar.close(); | ||
}); | ||
|
||
/* Prevent default browser behavior to allow drop on non-contenteditable elements */ | ||
this.Editor.Listeners.on(this.Editor.UI.nodes.holder, 'dragover', (e) => e.preventDefault(), true); | ||
} | ||
|
||
/** | ||
* Get and process tool`s drop configs | ||
* | ||
* @private | ||
*/ | ||
private processTools(): void { | ||
const tools = this.Editor.Tools.blockTools; | ||
|
||
Object.entries(tools).forEach(this.processTool); | ||
} | ||
|
||
/** | ||
* Check if onDrop config valid and save it to private property | ||
* | ||
* @param {string} name | ||
* @param {Tool} tool | ||
*/ | ||
private processTool = ([name, tool]): void => { | ||
const toolDropConfig = tool.onDrop; | ||
|
||
if (!toolDropConfig) { | ||
return; | ||
} | ||
|
||
const {handler} = toolDropConfig; | ||
let {extensions, mimeTypes} = toolDropConfig; | ||
|
||
if (!handler || typeof handler !== 'function') { | ||
_.log(`Drop handler for «${name}» Tool should be a function.`); | ||
return; | ||
} | ||
|
||
if (extensions && !Array.isArray(extensions)) { | ||
_.log(`«extensions» property of the onDrop config for «${name}» Tool should be an array`); | ||
extensions = []; | ||
} | ||
|
||
if (mimeTypes && !Array.isArray(mimeTypes)) { | ||
_.log(`«mimeTypes» property of the onDrop config for «${name}» Tool should be an array`); | ||
mimeTypes = []; | ||
} | ||
|
||
if (mimeTypes) { | ||
mimeTypes = mimeTypes.filter((type) => { | ||
if (!_.isValidMimeType(type)) { | ||
_.log(`MIME type value «${type}» for the «${name}» Tool is not a valid MIME type`, 'warn'); | ||
return false; | ||
} | ||
|
||
return true; | ||
}); | ||
} | ||
|
||
this.toolsDropConfigs[name] = { | ||
extensions: extensions || [], | ||
mimeTypes: mimeTypes || [], | ||
handler, | ||
}; | ||
} | ||
|
||
/** | ||
* Handle drop event | ||
* | ||
* @param {DragEvent} dropEvent | ||
*/ | ||
private processDrop = async (dropEvent: DragEvent): Promise<void> => { | ||
const { | ||
BlockManager, | ||
Paste, | ||
} = this.Editor; | ||
|
||
dropEvent.preventDefault(); | ||
|
||
BlockManager.blocks.forEach((block) => block.dropTarget = false); | ||
|
||
if (SelectionUtils.isAtEditor && this.isStartedAtEditor) { | ||
document.execCommand('delete'); | ||
} | ||
|
||
this.isStartedAtEditor = false; | ||
|
||
try { | ||
neSpecc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
BlockManager.setCurrentBlockByChildNode(dropEvent.target, 'end'); | ||
} catch (e) { | ||
BlockManager.setCurrentBlockByChildNode(BlockManager.lastBlock.holder, 'end'); | ||
} | ||
|
||
if (!dropEvent.dataTransfer.files.length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. надо оставить коммент |
||
const isHTML = dropEvent.dataTransfer.types.includes('text/html'); | ||
let data; | ||
|
||
if (isHTML) { | ||
data = dropEvent.dataTransfer.getData('text/html'); | ||
data = '<p>' + data + '</p>'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. нужен коммент |
||
} else { | ||
data = dropEvent.dataTransfer.getData('Text'); | ||
} | ||
|
||
Paste.processData(data, isHTML); | ||
|
||
return; | ||
} | ||
|
||
let dataToInsert = []; | ||
|
||
if (dropEvent.dataTransfer.files.length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. разве тут не всегда будет true? выше обратная проверка с return; |
||
dataToInsert = await Promise.all( | ||
Array | ||
.from(dropEvent.dataTransfer.items) | ||
.map((item) => this.processDataTransferItem(item)), | ||
); | ||
dataToInsert = dataToInsert.filter((data) => !!data); | ||
} | ||
|
||
dataToInsert.forEach( | ||
(data, i) => { | ||
if (i === 0 && BlockManager.currentBlock && BlockManager.currentBlock.isEmpty) { | ||
BlockManager.replace(data.type, data.data); | ||
return; | ||
} | ||
|
||
BlockManager.insert(data.type, data.data); | ||
}, | ||
); | ||
} | ||
|
||
/** | ||
* Handle dropped files | ||
* @param {DataTransferItem} item | ||
*/ | ||
private processDataTransferItem = async (item: DataTransferItem) => { | ||
if (item.kind === 'string') { | ||
return; | ||
} | ||
|
||
const file = item.getAsFile(); | ||
const extension = _.getFileExtension(file); | ||
|
||
const foundConfig = Object | ||
.entries(this.toolsDropConfigs) | ||
.find(([toolName, {mimeTypes, extensions}]) => { | ||
const [fileType, fileSubtype] = file.type.split('/'); | ||
|
||
const foundExt = extensions.find((ext) => ext.toLowerCase() === extension.toLowerCase()); | ||
const foundMimeType = mimeTypes.find((mime) => { | ||
const [type, subtype] = mime.split('/'); | ||
|
||
return type === fileType && (subtype === fileSubtype || subtype === '*'); | ||
}); | ||
|
||
return !!foundExt || !!foundMimeType; | ||
}); | ||
|
||
if (!foundConfig) { | ||
return; | ||
} | ||
|
||
const [tool, {handler}] = foundConfig; | ||
return { | ||
data: await handler(file), | ||
type: tool, | ||
}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
files pasted by drag'n'drop