完全なDOMコントロールを備えたReact用の柔軟なHTML5ドラッグアンドドロップミックスインであるreact-dndを実装しました。
既存のドラッグアンドドロップライブラリは私のユースケースに適合しなかったので、自分で作成しました。Stampsy.comで約1年間実行してきたコードに似ていますが、ReactとFluxを利用するために書き直されました。
私が持っていた主な要件:
- 独自のDOMまたはCSSをゼロにして、それを使用するコンポーネントに任せます。
- 消費するコンポーネントにできる限り少ない構造を課します。
- HTML5のドラッグアンドドロップをプライマリバックエンドとして使用しますが、将来的には異なるバックエンドを追加できるようになります。
- オリジナルのHTML5 APIと同様に、「ドラッグ可能なビュー」だけでなく、データのドラッグを強調します。
- 使用するコードからHTML5 APIの癖を隠します。
- さまざまなコンポーネントが、さまざまな種類のデータの「ドラッグソース」または「ドロップターゲット」になる場合があります。
- 必要に応じて、1つのコンポーネントに複数のドラッグソースとドロップターゲットを含めることができます。
- 互換性のあるデータがドラッグまたはホバーされている場合、ドロップターゲットの外観を簡単に変更できるようにします。
- 要素のスクリーンショットの代わりにドラッグサムネイルに画像を使いやすくし、ブラウザの癖を回避します。
これらがおなじみのように聞こえる場合は、読み進めてください。
使用法
単純なドラッグソース
最初に、ドラッグできるデータのタイプを宣言します。
これらは、ドラッグソースとドロップターゲットの「互換性」をチェックするために使用されます。
// 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
for が提供されItemTypes.IMAGE
ます。
A dragSource
は、ドラッグソースの動作を指定する単なるオブジェクトです。beginDrag
ドラッグしているデータを表すアイテムを返すように実装し、必要に応じて、ドラッグしているUIを調整するいくつかのオプションを実装する必要があります。必要に応じcanDrag
て、ドラッグを禁止endDrag(didDrop)
するか、ドロップが発生した(または発生していない)ときにロジックを実行するように実装できます。また、コンポーネントdragSource
用に共有ミックスインを生成させることで、コンポーネント間でこのロジックを共有できます。
最後に、ドラッグハンドラをアタッチするには{...this.dragSourceFor(itemType)}
、の一部(1つ以上)の要素を使用する必要がありrender
ます。これは、1つの要素に複数の「ドラッグハンドル」を含めることができ、それらが異なる項目タイプに対応している場合があることを意味します。(JSXスプレッド属性の構文に慣れていない場合は、チェックしてください)。
単純なドロップターゲット
sのImageBlock
ドロップターゲットになりたいとしましょうIMAGE
。それは我々が与える必要があることを除いて、ほとんど同じだ実装を: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>
);
}
);
1つのコンポーネントでソースをドラッグ+ターゲットをドロップ
ユーザーが画像をからドラッグアウトできるようにしたいとします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)
一度に複数のタイプを指定できるため、1つのドロップゾーンで多くの異なるタイプをキャッチできます。
- より細かい制御が必要な場合、ほとんどのメソッドには最後のパラメーターとしてそれらを引き起こしたドラッグイベントが渡されます。
最新のドキュメントとインストール手順については、Githubのreact-dndリポジトリにアクセスしてください。