add nova
This commit is contained in:
113
nova/resources/js/components/DropZone/DropZone.vue
Normal file
113
nova/resources/js/components/DropZone/DropZone.vue
Normal file
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div>
|
||||
<input
|
||||
class="visually-hidden"
|
||||
:dusk="$attrs['input-dusk']"
|
||||
@change.prevent="handleChange"
|
||||
type="file"
|
||||
ref="fileInput"
|
||||
:multiple="multiple"
|
||||
:accept="acceptedTypes"
|
||||
:disabled="disabled"
|
||||
tabindex="-1"
|
||||
/>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div v-if="files.length > 0" class="grid grid-cols-4 gap-x-6 gap-y-2">
|
||||
<FilePreviewBlock
|
||||
v-for="(file, index) in files"
|
||||
:file="file"
|
||||
@removed="() => handleRemove(index)"
|
||||
:rounded="rounded"
|
||||
:dusk="$attrs.dusk"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
tabindex="0"
|
||||
role="button"
|
||||
@click="handleClick"
|
||||
@keydown.space.prevent="handleClick"
|
||||
@keydown.enter.prevent="handleClick"
|
||||
class="focus:outline-none focus:!border-primary-500 block cursor-pointer p-4 bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-900 border-4 border-dashed hover:border-gray-300 dark:border-gray-700 dark:hover:border-gray-600 rounded-lg"
|
||||
:class="{ 'border-gray-300 dark:border-gray-600': startedDrag }"
|
||||
@dragenter.prevent="handleOnDragEnter"
|
||||
@dragleave.prevent="handleOnDragLeave"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleOnDrop"
|
||||
>
|
||||
<div class="flex items-center space-x-4 pointer-events-none">
|
||||
<p class="text-center pointer-events-none">
|
||||
<Button as="div">
|
||||
{{ multiple ? __('Choose Files') : __('Choose File') }}
|
||||
</Button>
|
||||
</p>
|
||||
|
||||
<p
|
||||
class="pointer-events-none text-center text-sm text-gray-500 dark:text-gray-400 font-semibold"
|
||||
>
|
||||
{{
|
||||
multiple
|
||||
? __('Drop files or click to choose')
|
||||
: __('Drop file or click to choose')
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useLocalization } from '@/composables/useLocalization'
|
||||
import { useDragAndDrop } from '@/composables/useDragAndDrop'
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
const emit = defineEmits(['fileChanged', 'fileRemoved'])
|
||||
const { __ } = useLocalization()
|
||||
|
||||
const props = defineProps({
|
||||
files: { type: Array, default: [] },
|
||||
multiple: { type: Boolean, default: false },
|
||||
rounded: { type: Boolean, default: false },
|
||||
acceptedTypes: { type: String, default: null },
|
||||
disabled: { type: Boolean, default: false },
|
||||
})
|
||||
|
||||
const { startedDrag, handleOnDragEnter, handleOnDragLeave } =
|
||||
useDragAndDrop(emit)
|
||||
|
||||
const demFiles = ref([])
|
||||
const fileInput = ref()
|
||||
|
||||
const handleClick = () => fileInput.value.click()
|
||||
|
||||
const handleOnDrop = e => {
|
||||
demFiles.value = props.multiple
|
||||
? e.dataTransfer.files
|
||||
: [e.dataTransfer.files[0]]
|
||||
|
||||
emit('fileChanged', demFiles.value)
|
||||
}
|
||||
|
||||
const handleChange = () => {
|
||||
demFiles.value = props.multiple
|
||||
? fileInput.value.files
|
||||
: [fileInput.value.files[0]]
|
||||
emit('fileChanged', demFiles.value)
|
||||
fileInput.value.files = null
|
||||
}
|
||||
|
||||
const handleRemove = index => {
|
||||
emit('fileRemoved', index)
|
||||
fileInput.value.files = null
|
||||
fileInput.value.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
}
|
||||
</script>
|
||||
86
nova/resources/js/components/DropZone/FilePreviewBlock.vue
Normal file
86
nova/resources/js/components/DropZone/FilePreviewBlock.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="h-full flex items-start justify-center">
|
||||
<div class="relative w-full">
|
||||
<!-- Remove Button -->
|
||||
<RemoveButton
|
||||
v-if="removable"
|
||||
class="absolute z-20 top-[-10px] right-[-9px]"
|
||||
@click.stop="handleRemoveClick"
|
||||
v-tooltip="__('Remove')"
|
||||
:dusk="$attrs.dusk"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="bg-gray-50 dark:bg-gray-700 relative aspect-square flex items-center justify-center border-2 border-gray-200 dark:border-gray-700 overflow-hidden rounded-lg"
|
||||
>
|
||||
<!-- Upload Overlay -->
|
||||
<div
|
||||
v-if="file.processing"
|
||||
class="absolute inset-0 flex items-center justify-center"
|
||||
>
|
||||
<ProgressBar
|
||||
:title="uploadingLabel"
|
||||
class="mx-4"
|
||||
color="bg-green-500"
|
||||
:value="uploadingPercentage"
|
||||
/>
|
||||
<div class="bg-primary-900 opacity-5 absolute inset-0" />
|
||||
</div>
|
||||
|
||||
<!-- Image Preview -->
|
||||
<img
|
||||
v-if="isImage"
|
||||
:src="previewUrl"
|
||||
class="aspect-square object-scale-down"
|
||||
/>
|
||||
<div v-else>
|
||||
<div class="rounded bg-gray-200 border-2 border-gray-200 p-4">
|
||||
<Icon type="document-text" width="50" height="50" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Information -->
|
||||
<p class="font-semibold text-xs mt-1">{{ file.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFilePreviews } from '@/composables/useFilePreviews'
|
||||
import { useLocalization } from '@/composables/useLocalization'
|
||||
import { computed, toRef } from 'vue'
|
||||
|
||||
const { __ } = useLocalization()
|
||||
const emit = defineEmits(['removed'])
|
||||
const props = defineProps({
|
||||
file: { type: Object },
|
||||
removable: { type: Boolean, default: true },
|
||||
})
|
||||
|
||||
const uploadingLabel = computed(() => {
|
||||
if (props.file.processing) {
|
||||
return __('Uploading') + ' (' + props.file.progress + '%)'
|
||||
}
|
||||
|
||||
return props.file.name
|
||||
})
|
||||
|
||||
const uploadingPercentage = computed(() => {
|
||||
if (props.file.processing) {
|
||||
return props.file.progress
|
||||
}
|
||||
|
||||
return 100
|
||||
})
|
||||
|
||||
const { previewUrl, isImage } = useFilePreviews(toRef(props, 'file'))
|
||||
|
||||
const handleRemoveClick = () => emit('removed')
|
||||
</script>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
}
|
||||
</script>
|
||||
65
nova/resources/js/components/DropZone/SingleDropZone.vue
Normal file
65
nova/resources/js/components/DropZone/SingleDropZone.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div v-if="files.length > 0" class="grid grid-cols-4 gap-x-6">
|
||||
<FilePreviewBlock
|
||||
v-for="(file, index) in files"
|
||||
:file="file"
|
||||
@removed="() => handleRemoveClick(index)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@click="handleClick"
|
||||
class="cursor-pointer p-4 bg-gray-50 dark:bg-gray-900 dark:hover:bg-gray-900 border-4 border-dashed hover:border-gray-300 dark:hover:border-gray-600 rounded-lg"
|
||||
:class="
|
||||
startedDrag
|
||||
? 'border-gray-300 dark:border-gray-600'
|
||||
: 'border-gray-200 dark:border-gray-700'
|
||||
"
|
||||
@dragenter.prevent="handleOnDragEnter"
|
||||
@dragleave.prevent="handleOnDragLeave"
|
||||
@dragover.prevent
|
||||
@drop.prevent="handleOnDrop"
|
||||
>
|
||||
<div class="flex items-center space-x-4">
|
||||
<p class="text-center pointer-events-none">
|
||||
<Button as="div">
|
||||
{{ __('Choose a file') }}
|
||||
</Button>
|
||||
</p>
|
||||
|
||||
<p
|
||||
class="pointer-events-none text-center text-sm text-gray-500 dark:text-gray-400 font-semibold"
|
||||
>
|
||||
{{
|
||||
multiple
|
||||
? __('Drop files or click to choose')
|
||||
: __('Drop file or click to choose')
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useLocalization } from '@/composables/useLocalization'
|
||||
import { useDragAndDrop } from '@/composables/useDragAndDrop'
|
||||
import { Button } from 'laravel-nova-ui'
|
||||
|
||||
const { __ } = useLocalization()
|
||||
|
||||
const emit = defineEmits(['fileChanged', 'fileRemoved'])
|
||||
|
||||
const { startedDrag, handleOnDragEnter, handleOnDragLeave, handleOnDrop } =
|
||||
useDragAndDrop(emit)
|
||||
|
||||
defineProps({
|
||||
files: Array,
|
||||
handleClick: Function,
|
||||
})
|
||||
|
||||
function handleRemoveClick(index) {
|
||||
emit('fileRemoved', index)
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user