Я реалізував response-dnd , гнучкий мікшин перетягування HTML5 для React з повним контролем DOM.
Існуючі бібліотеки перетягування і падіння не відповідали моєму варіанту використання, тому я написав свою власну. Це схоже на код, який ми працюємо близько року на Stampsy.com, але переписаний, щоб скористатися перевагами React та Flux.
Основні вимоги до мене:
- Випускає нуль DOM або CSS самостійно, залишаючи це споживаючим компонентам;
- Накладати якомога менше структури на споживаючі компоненти;
- Використовуйте перетягування HTML5 як основне серверне середовище, але дайте можливість додавати різні серверні файли в майбутньому;
- Як і оригінальний API HTML5, наголошуйте на перетягуванні даних, а не просто на "перетягуваних переглядах";
- Приховати примхи API HTML5 від споживаного коду;
- Різні компоненти можуть бути "джерелами перетягування" або "цілями скидання" для різних типів даних;
- Дозволити одному компоненту містити кілька джерел перетягування та скидати цілі, коли це необхідно;
- Зробіть полегшеним для скидання цілей зміну зовнішнього вигляду, якщо сумісні дані перетягуються або наводяться;
- Спростіть використання зображень для перетягування ескізів замість скріншотів елементів, обходячи примхи браузера.
Якщо вони вам звучать звично, читайте далі.
Використання
Просте джерело перетягування
Спочатку оголосіть типи даних, які можна перетягувати.
Вони використовуються для перевірки "сумісності" джерел перетягування та цілей скидання:
// ItemTypes.js
module.exports = {
BLOCK: 'block',
IMAGE: 'image'
};
(Якщо у вас немає декількох типів даних, ця бібліотека може бути не для вас.)
Тоді давайте зробимо дуже простий компонент, який можна перетягнути, який при перетягуванні представляє IMAGE:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var Image = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
// Specify all supported types by calling registerType(type, { dragSource?, dropTarget? })
registerType(ItemTypes.IMAGE, {
// dragSource, when specified, is { beginDrag(), canDrag()?, endDrag(didDrop)? }
dragSource: {
// beginDrag should return { item, dragOrigin?, dragPreview?, dragEffect? }
beginDrag() {
return {
item: this.props.image
};
}
}
});
},
render() {
// {...this.dragSourceFor(ItemTypes.IMAGE)} will expand into
// { draggable: true, onDragStart: (handled by mixin), onDragEnd: (handled by mixin) }.
return (
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
);
}
);
Вказуючи configureDragDrop, ми DragDropMixinвизначаємо поведінку перетягування цього компонента. Як компоненти, що перетягуються, так і елементи, що випадають, використовують один і той же мікс.
Усередині configureDragDropнам потрібно зателефонувати registerTypeкожному нашому користувачеві, ItemTypesякий підтримує компонент. Наприклад, у вашому додатку може бути кілька зображень зображень, і кожна з них надає файлdragSource для ItemTypes.IMAGE.
A dragSource- це просто об'єкт, який вказує, як працює джерело перетягування. Ви повинні реалізувати, beginDragщоб повернути елемент, який представляє дані, які ви перетягуєте, і, за бажанням, кілька параметрів, які регулюють інтерфейс перетягування. Ви можете додатково реалізувати, canDragщоб заборонити перетягування або endDrag(didDrop)виконати певну логіку, коли падіння відбулося (або не відбулося). І ви можете поділити цю логіку між компонентами, дозволивши спільному змішуванню генеруватиdragSource для них.
Нарешті, ви повинні використовувати {...this.dragSourceFor(itemType)}деякі (один або кілька) елементів, renderщоб приєднати обробники перетягування. Це означає, що ви можете мати кілька «ручок перетягування» в одному елементі, і вони можуть навіть відповідати різним типам предметів. (Якщо ви не знайомі з синтаксисом розповсюдження атрибутів JSX , перевірте його).
Проста ціль падіння
Скажімо, ми хочемо ImageBlockбути ціллю зниження для IMAGEs. Це майже те ж саме, за винятком того, що ми повинні дати registerTypeв dropTargetреалізації:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
// dropTarget, when specified, is { acceptDrop(item)?, enter(item)?, over(item)?, leave(item)? }
dropTarget: {
acceptDrop(image) {
// Do something with image! for example,
DocumentActionCreators.setImage(this.props.blockId, image);
}
}
});
},
render() {
// {...this.dropTargetFor(ItemTypes.IMAGE)} will expand into
// { onDragEnter: (handled by mixin), onDragOver: (handled by mixin), onDragLeave: (handled by mixin), onDrop: (handled by mixin) }.
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{this.props.image &&
<img src={this.props.image.url} />
}
</div>
);
}
);
Перетягніть джерело + скиньте ціль в один компонент
Скажімо, ми тепер хочемо, щоб користувач міг витягнути зображення з ImageBlock. Нам просто потрібно додати відповідне dragSourceдо нього та кілька обробників:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
// Add a drag source that only works when ImageBlock has an image:
dragSource: {
canDrag() {
return !!this.props.image;
},
beginDrag() {
return {
item: this.props.image
};
}
}
dropTarget: {
acceptDrop(image) {
DocumentActionCreators.setImage(this.props.blockId, image);
}
}
});
},
render() {
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{/* Add {...this.dragSourceFor} handlers to a nested node */}
{this.props.image &&
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
}
</div>
);
}
);
Що ще можливо?
Я не все висвітлив, але можна використовувати цей API ще кількома способами:
- Використовуйте
getDragState(type)таgetDropState(type) щоб дізнатися, чи активне перетягування, і використовуйте його для перемикання класів або атрибутів CSS;
- Вкажіть
dragPreviewдля Imageвикористання зображень як заповнювачів перетягування (використовуйте ImagePreloaderMixinдля їх завантаження);
- Скажімо, ми хочемо зробити
ImageBlocksдоступними для замовлення. Вони нам потрібні лише для реалізації dropTargetта dragSourceдля ItemTypes.BLOCK.
- Припустимо, ми додаємо інші види блоків. Ми можемо повторно використати їх логіку переупорядкування, розмістивши її в міксіні.
dropTargetFor(...types) дозволяє вказати кілька типів одночасно, тому одна зона падіння може вловлювати багато різних типів.
- Коли вам потрібен більш детальний контроль, більшість методів передаються подією перетягування, яка спричинила їх як останній параметр.
Щоб отримати сучасну документацію та інструкції з встановлення, перейдіть до реакції та репо на Github .