DropZone
ドロップされたファイルを受け取り、File として渡す領域。
- React
- Svelte
- Vue
function App() { const [files, setFiles] = useState<File[]>([]) const [rejected, setRejected] = useState<File[]>([]) return ( <div> <DropZone.Root className={dropZoneTheme.root} accept="audio/*" multiple onDrop={(files) => { setRejected([]) setFiles(files) }} onReject={(files) => setRejected(files)} > Drop an audio file here </DropZone.Root> <ul> {files.map((file) => ( <li key={file.name}> {file.name} ({file.type || 'unknown type'}) </li> ))} </ul> {rejected.length > 0 && ( <p>Not audio: {rejected.map((file) => file.name).join(', ')}</p> )} </div> ) }
<script lang="ts">
import { DropZone } from '@tremolo-ui/svelte'
// Copy this file from the Styling page into your own project.
import dropZoneTheme from './DropZone.module.css'
let files: File[] = $state([])
let rejected: File[] = $state([])
</script>
<DropZone.Root
class={dropZoneTheme.root}
accept="audio/*"
multiple
onDrop={(dropped) => {
rejected = []
files = dropped
}}
onReject={(dropped) => (rejected = dropped)}
>
Drop an audio file here
</DropZone.Root>
<ul>
{#each files as file}
<li>{file.name} ({file.type || 'unknown type'})</li>
{/each}
</ul>
{#if rejected.length > 0}
<p>Not audio: {rejected.map((file) => file.name).join(', ')}</p>
{/if}
<script setup lang="ts">
import { ref } from 'vue'
import { DropZone } from '@tremolo-ui/vue'
// Copy this file from the Styling page into your own project.
import dropZoneTheme from './DropZone.module.css'
const files = ref<File[]>([])
const rejected = ref<File[]>([])
function onDrop(dropped: File[]) {
rejected.value = []
files.value = dropped
}
</script>
<template>
<DropZone
:class="dropZoneTheme.root"
accept="audio/*"
multiple
@drop="onDrop"
@reject="(dropped) => (rejected = dropped)"
>
Drop an audio file here
</DropZone>
<ul>
<li v-for="(file, i) in files" :key="i">
{{ file.name }} ({{ file.type || 'unknown type' }})
</li>
</ul>
<p v-if="rejected.length > 0">
Not audio: {{ rejected.map((file) => file.name).join(', ') }}
</p>
</template>
インポート
- React
- Svelte
- Vue
import { DropZone } from '@tremolo-ui/react'
import { DropZone } from '@tremolo-ui/svelte'
import { DropZone } from '@tremolo-ui/vue'
このコンポーネントが引き受けること
子要素から出てもドラッグは終わりません。 dragenter と dragleave は子孫でも発火するので、ゾーンの中で 2 つの子の間を移動すると、入る前に出ることになります。組になった回数を数えることで、要素の真ん中で [data-dragover] が点滅するのを防いでいます。
拒否するときもドロップはキャンセルします。 誰も処理しないドロップは、ブラウザがページを離れてそのファイルを開いてしまい、アプリが抱えていたものが失われます。ファイルを受け取るかどうかに関わらず、ゾーンはドロップを飲み込みます。
別の場所で終わったドラッグでも状態は戻ります。 他の要素にドロップされた、Escape でキャンセルされた、ウィンドウの外へ出た。いずれも dragleave を送らないので、そのままでは要素がドロップ先として印を付けられたままになります。
ドロップ前に拒否する
accept は 2 回照合されます。ドロップ時には、どのファイルが onDrop へ行き、どれが onReject へ行くかを決めます。ドラッグがまだ空中にある間は [data-invalid] を立てるので、手を離す前に「これは受け取らない」と示せます。
ドロップ前に見分けられる範囲には限りがあります。 ブラウザはドラッグされているもののタイプは報告しますが、名前は伏せます。そのため拡張子で書いた規則(.wav)はまだ判定できません。ドラッグに対して不利には数えず、名前が分かるドロップ時に判定します。MIME タイプで書いた規則(audio/* / audio/wav)はどちらでも働きます。
任意の要素をドロップ先にする
DropZone.Root が描くのは <div> です。要素が既にある場合(波形表示、トラックのレーン、canvas)は、useDropZone hook が包まずに同じ仕事をします。
const { refCallback, over, invalid } = useDropZone({
accept: 'audio/*',
onDrop: (files) => load(files[0]),
})
return <canvas ref={refCallback} data-dragover={over ? '' : undefined} />
オプションは children 以外同じで、DropZone.Root が data 属性を描くもとにしている状態を返します。
| Prop | 型 | 既定値 |
|---|---|---|
accept | string | — |
multiple | boolean | false |
disabled | boolean | false |
onDrop | (files: File[], event: DragEvent) => void | — |
onReject | (files: File[], event: DragEvent) => void | — |
すべての説明: UseDropZoneOptions
クリックで選ばせたいとき
ボタンからファイルピッカーを開くには FileInput を見てください。同じ File[] を報告します。
API リファレンス
各パートには、そのパート固有の props があります。描画する要素の属性(className や style など)も受け取ります。
Svelte / Vue でも props は同じで、書き方だけがそれぞれの流儀になります(className ではなく class、bind:value や v-model、中身は snippet やスロットなど)。Svelte と Vue を参照してください。
状態は data 属性として要素に付くので、セレクタひとつで届きます(スタイリング)。属性は、その状態が on のときだけ存在します。
Root
領域そのもの。設定を持ち、ドロップされたものを報告します。
| Prop | 型 | 既定値 |
|---|---|---|
accept | string | — |
multiple | boolean | false |
disabled | boolean | false |
onDrop | (files: File[], event: DragEvent) => void | — |
onReject | (files: File[], event: DragEvent) => void | — |
children* | ReactNode | — |
すべての説明: DropZoneProps
| Data attribute | Values |
|---|---|
[data-dragover] | ファイルがゾーンの上にドラッグされている間、存在します |
[data-invalid] | ドラッグされているものが、今判定できる accept の規則のどれにも一致しない間、存在します |
[data-disabled] | disabled が設定されているときに存在します |