Zaimplementowałem react-dnd , elastyczną mieszankę HTML5 typu przeciągnij i upuść dla Reacta z pełną kontrolą DOM.
Istniejące biblioteki typu „przeciągnij i upuść” nie pasowały do mojego przypadku użycia, więc napisałem własną. Jest podobny do kodu, który używamy od około roku na Stampsy.com, ale został przepisany, aby wykorzystać React i Flux.
Kluczowe wymagania jakie miałem:
- Emituj zero własnego DOM lub CSS, pozostawiając to konsumującym komponentom;
- Nakładaj jak najmniejszą strukturę na zużywane komponenty;
- Użyj funkcji przeciągnij i upuść HTML5 jako podstawowego zaplecza, ale umożliwisz dodawanie różnych backendów w przyszłości;
- Podobnie jak oryginalny interfejs API HTML5, kładź nacisk na przeciąganie danych, a nie tylko na „przeciągalne widoki”;
- Ukryj dziwactwa interfejsu API HTML5 przed konsumującym kodem;
- Różne komponenty mogą być „źródłami przeciągania” lub „celami upuszczania” dla różnych rodzajów danych;
- Pozwól, aby jeden komponent zawierał kilka źródeł przeciągania i upuszczania celów w razie potrzeby;
- Ułatw celom upuszczania zmianę ich wyglądu, jeśli zgodne dane są przeciągane lub najeżdżane;
- Ułatw korzystanie z obrazów do przeciągania miniatur zamiast zrzutów ekranu elementów, omijając dziwactwa przeglądarki.
Jeśli to brzmi znajomo, czytaj dalej.
Stosowanie
Proste źródło przeciągania
Najpierw zadeklaruj typy danych, które można przeciągać.
Służą one do sprawdzania „zgodności” źródeł przeciągania i celów upuszczania:
module.exports = {
BLOCK: 'block',
IMAGE: 'image'
};
(Jeśli nie masz wielu typów danych, ta biblioteka może nie być dla Ciebie).
Następnie stwórzmy bardzo prosty komponent do przeciągania, który po przeciągnięciu reprezentuje IMAGE:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var Image = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
dragSource: {
beginDrag() {
return {
item: this.props.image
};
}
}
});
},
render() {
return (
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
);
}
);
Podając specyfikację configureDragDrop, mówimy o DragDropMixinzachowaniu tego komponentu przez przeciąganie i upuszczanie. Komponenty, które można przeciągać i upuszczać, używają tego samego miksu.
Wewnątrz configureDragDropmusimy zadzwonić registerTypedo każdego z naszych niestandardowych ItemTypesobsługiwanych przez ten składnik. Na przykład, nie może być kilka reprezentacje obrazów w aplikacji, a każda będzie dostarczenie dragSourcedo ItemTypes.IMAGE.
A dragSourceto po prostu obiekt określający sposób działania źródła przeciągania. Musisz zaimplementować, beginDragaby zwrócić element, który reprezentuje przeciągane dane i opcjonalnie kilka opcji, które dostosowują przeciągany interfejs użytkownika. Opcjonalnie możesz zaimplementować, canDragaby zabronić przeciągania lub endDrag(didDrop)wykonać jakąś logikę, gdy upuszczenie wystąpiło (lub nie). Możesz dzielić tę logikę między komponentami, pozwalając na generowanie dragSourcedla nich współdzielonego miksu .
Na koniec musisz użyć {...this.dragSourceFor(itemType)}na niektórych (jednym lub więcej) elementach programu, renderaby dołączyć uchwyty przeciągania. Oznacza to, że możesz mieć kilka „uchwytów przeciągania” w jednym elemencie i mogą one nawet odpowiadać różnym typom elementów. (Jeśli nie znasz składni JSX Spread Attributes , sprawdź to).
Prosty cel upuszczenia
Powiedzmy, że chcemy ImageBlockbyć celem upuszczenia dla IMAGEs. To niemal tak samo, z tym że musimy dać registerTypedo dropTargetrealizacji:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
dropTarget: {
acceptDrop(image) {
DocumentActionCreators.setImage(this.props.blockId, image);
}
}
});
},
render() {
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{this.props.image &&
<img src={this.props.image.url} />
}
</div>
);
}
);
Przeciągnij źródło + upuść cel w jednym komponencie
Powiedzmy, że teraz chcemy, aby użytkownik mógł wyciągnąć obraz z ImageBlock. Musimy tylko dodać dragSourcedo niego odpowiednie i kilka handlerów:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.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>
);
}
);
Co jeszcze jest możliwe?
Nie opisałem wszystkiego, ale można użyć tego API na kilka innych sposobów:
- Użyj
getDragState(type)i, getDropState(type)aby dowiedzieć się, czy przeciąganie jest aktywne, i użyj go do przełączania klas lub atrybutów CSS;
- Określ,
dragPreviewaby Imageużywać obrazów jako symboli zastępczych przeciągania (użyj, ImagePreloaderMixinaby je załadować);
- Powiedzmy, że chcemy, aby można było je ponownie
ImageBlockszamówić. Potrzebujemy ich tylko do wdrożenia dropTargeti dragSourcedla ItemTypes.BLOCK.
- Załóżmy, że dodamy inne rodzaje bloków. Możemy ponownie wykorzystać ich logikę zmiany kolejności, umieszczając ją w miksie.
dropTargetFor(...types) pozwala określić kilka typów na raz, więc jedna strefa zrzutu może złapać wiele różnych typów.
- Gdy potrzebujesz bardziej szczegółowej kontroli, większość metod jest przekazywana jako ostatni parametr jako zdarzenie przeciągania, które je spowodowało.
Aby uzyskać aktualną dokumentację i instrukcje instalacji, przejdź na stronę react-dnd repo na Github .