Я реалізував 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
бути ціллю зниження для IMAGE
s. Це майже те ж саме, за винятком того, що ми повинні дати 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 .