forked from platypush/platypush
Added FileSelector
UI component.
This commit is contained in:
parent
bac06e9e7b
commit
d4f6d174c8
2 changed files with 154 additions and 5 deletions
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row item" v-for="(file, i) in filteredFiles" :key="i" @click="path = file.path">
|
<div class="row item" v-for="(file, i) in filteredFiles" :key="i" @click="onItemSelect(file)">
|
||||||
<div class="col-10">
|
<div class="col-10">
|
||||||
<i class="icon fa" :class="{'fa-file': file.type !== 'directory', 'fa-folder': file.type === 'directory'}" />
|
<i class="icon fa" :class="{'fa-file': file.type !== 'directory', 'fa-folder': file.type === 'directory'}" />
|
||||||
<span class="name">
|
<span class="name">
|
||||||
|
@ -35,11 +35,11 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-2 actions">
|
<div class="col-2 actions" v-if="fileActions.length">
|
||||||
<Dropdown>
|
<Dropdown>
|
||||||
<DropdownItem icon-class="fa fa-play" text="Play"
|
<DropdownItem icon-class="fa fa-play" text="Play"
|
||||||
@click="$emit('play', {type: 'file', url: `file://${file.path}`})"
|
@click="$emit('play', {type: 'file', url: `file://${file.path}`})"
|
||||||
v-if="isMedia && mediaExtensions.has(file.name.split('.').pop()?.toLowerCase())" />
|
v-if="hasPlay && file.type !== 'directory'" />
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,7 +58,7 @@ export default {
|
||||||
name: "Browser",
|
name: "Browser",
|
||||||
components: {DropdownItem, Dropdown, Loading},
|
components: {DropdownItem, Dropdown, Loading},
|
||||||
mixins: [Utils, MediaUtils],
|
mixins: [Utils, MediaUtils],
|
||||||
emits: ['back', 'path-change', 'play'],
|
emits: ['back', 'path-change', 'play', 'input'],
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
hasBack: {
|
hasBack: {
|
||||||
|
@ -96,6 +96,23 @@ export default {
|
||||||
return this.files.filter((file) => (file?.name || '').toLowerCase().indexOf(this.filter.toLowerCase()) >= 0)
|
return this.files.filter((file) => (file?.name || '').toLowerCase().indexOf(this.filter.toLowerCase()) >= 0)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hasPlay() {
|
||||||
|
return this.isMedia && this.files.some((file) => this.mediaExtensions.has(file.name.split('.').pop()?.toLowerCase()))
|
||||||
|
},
|
||||||
|
|
||||||
|
fileActions() {
|
||||||
|
if (!this.hasPlay)
|
||||||
|
return []
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
iconClass: 'fa fa-play',
|
||||||
|
text: 'Play',
|
||||||
|
onClick: (file) => this.$emit('play', {type: 'file', url: `file://${file.path}`}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
pathTokens() {
|
pathTokens() {
|
||||||
if (!this.path?.length)
|
if (!this.path?.length)
|
||||||
return ['/']
|
return ['/']
|
||||||
|
@ -128,10 +145,26 @@ export default {
|
||||||
else
|
else
|
||||||
this.path = [...this.pathTokens].slice(0, -1).join('/').slice(1)
|
this.path = [...this.pathTokens].slice(0, -1).join('/').slice(1)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onItemSelect(file) {
|
||||||
|
if (file.type === 'directory')
|
||||||
|
this.path = file.path
|
||||||
|
else
|
||||||
|
this.$emit('input', file.path)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
initialPath() {
|
||||||
|
this.path = this.initialPath
|
||||||
|
},
|
||||||
|
|
||||||
|
path() {
|
||||||
|
this.refresh()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$watch(() => this.path, () => this.refresh())
|
|
||||||
this.refresh()
|
this.refresh()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
<template>
|
||||||
|
<div class="file-selector-container">
|
||||||
|
<div class="input">
|
||||||
|
<input type="text"
|
||||||
|
:value="value"
|
||||||
|
:readonly="strict"
|
||||||
|
@input="$emit('input', $event.target.value)" />
|
||||||
|
|
||||||
|
<button type="button"
|
||||||
|
title="Select a file"
|
||||||
|
@click="$refs.fileSelectorModal.show()">
|
||||||
|
<i class="fa fa-folder-open" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal title="Select a file" ref="fileSelectorModal">
|
||||||
|
<Browser :initialPath="path"
|
||||||
|
@input="onValueChange($event)"
|
||||||
|
@path-change="path = $event" />
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Modal from "@/components/Modal";
|
||||||
|
import Browser from "@/components/File/Browser";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
emits: ['input'],
|
||||||
|
components: {
|
||||||
|
Browser,
|
||||||
|
Modal,
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
strict: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
path: '/',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onValueChange(value) {
|
||||||
|
this.$emit('input', value)
|
||||||
|
},
|
||||||
|
|
||||||
|
onFileSelect(value) {
|
||||||
|
if (value != null && (value.startsWith('/') || value.startsWith('file://')))
|
||||||
|
this.path = value.split('/').slice(0, -1).join('/')
|
||||||
|
else
|
||||||
|
this.path = '/'
|
||||||
|
|
||||||
|
this.$refs.fileSelectorModal.hide()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value(value) {
|
||||||
|
this.onFileSelect(value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.onFileSelect(this.value)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.file-selector-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.input {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
|
input {
|
||||||
|
flex-grow: 1;
|
||||||
|
border-radius: 1em 0 0 1em;
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 0 1em 1em 0;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.modal) {
|
||||||
|
.body {
|
||||||
|
width: 80vw;
|
||||||
|
height: 80vh;
|
||||||
|
max-width: 800px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.items {
|
||||||
|
.item {
|
||||||
|
padding: 1em 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in a new issue