Skip to content

Commit

Permalink
Merge pull request #33 from DreamlandOwO/master
Browse files Browse the repository at this point in the history
Request transform, feature select and api changes
  • Loading branch information
n1crack authored Jan 30, 2024
2 parents 458b278 + 47514ae commit 12bc814
Show file tree
Hide file tree
Showing 36 changed files with 727 additions and 202 deletions.
54 changes: 41 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,52 @@ app.use(VueFinder)
app.mount('#app')

```
Html
```html
Vue Template
```vue
...
<div>
<vue-finder id='my_vuefinder' url="http://vuefinder-php.test"></vue-finder>
<vue-finder id='my_vuefinder' :request="request"></vue-finder>
</div>
...
<script setup>
const request = "http://vuefinder-php.test"
// Or ...
const request = {
// ----- CHANGE ME! -----
// [REQUIRED] Url for development server endpoint
baseUrl: "http://vuefinder-php.test",
// ----- CHANGE ME! -----
// Additional headers & params & body
headers: { "X-ADDITIONAL-HEADER": 'yes' },
params: { additionalParam1: 'yes' },
body: { additionalBody1: ['yes'] },
// And/or transform request callback
transformRequest: req => {
if (req.method === 'get') {
req.params.vf = "1"
}
return req;
},
// XSRF Token header name
xsrfHeaderName: "X-CSRF-TOKEN",
}
</script>
```

### Props

| Prop | Value | Default | Description |
|---------------|:-------:|---------|:---------------------------------------|
| id | string | _null_ | required |
| url | string | _null_ | required - backend url |
| locale | string | en | optional - default language code |
| dark | boolean | false | optional - makes theme dark as default |
| max-file-size | string | 10mb | optional - client side max file upload |
| Prop | Value | Default | Description |
|---------------|:-------:|---------|:----------------------------------------------------|
| id | string | _null_ | required |
| request | object | _null_ | required - backend url or request object, see above |
| locale | string | en | optional - default language code |
| dark | boolean | false | optional - makes theme dark as default |
| max-file-size | string | 10mb | optional - client side max file upload |

### Features
- Multi adapter/storage (see https://github.com/thephpleague/flysystem)
Expand Down Expand Up @@ -86,13 +114,13 @@ Html
- PHP: [VueFinder Php Library](https://github.com/n1crack/vuefinder-php)

### Roadmap
- [x] restyle the modals
- [x] add more languages (only en/tr/ru at the moment. PRs are welcomed.)
- [x] emit select event, with @select get selected files for online editors like tinymce/ckeditor
- [ ] code refactoring (cleanup the duplications, make reusable components)
- [ ] restyle the modals
- [ ] add more languages (only en/tr/ru at the moment. PRs are welcomed.)
- [ ] copy/move to a folder (modal, treeview)
- [ ] transfer items between filesystem adapters
- [ ] show/hide components (toolbar/statusbar etc.)
- [ ] emit select event, with @select get selected files for online editors like tinymce/ckeditor
- [ ] drag&drop on folders at address bar
- [ ] update DragSelect plugin for using its dropzone support

Expand Down
75 changes: 71 additions & 4 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,75 @@
<div class="wrapper">
<vue-finder
id='my_vuefinder'
:request="request"
:max-file-size="maxFileSize"
:url="url"
:features="features"
@select="handleSelect"
/>

<button class="btn" @click="handleButton" :disabled="!selectedFiles.length">Show Selected</button>

<div v-show="selectedFiles.length">
<h3>Selected Files</h3>
<ul>
<li v-for="file in selectedFiles" :key="file.path">
{{ file.path }}
</li>
</ul>
</div>

</div>
</template>

<script setup>
import { ref } from 'vue';
import { FEATURES, FEATURE_ALL_NAMES } from './components/features.js';
/** @type {import('./utils/ajax.js').RequestConfig} */
const request = {
// ----- CHANGE ME! -----
// [REQUIRED] Url for development server endpoint
baseUrl: "http://localhost:8005/",
// ----- CHANGE ME! -----
// CHANGE ME, Url for development server endpoint
const url = ref("http://localhost:8005/")
// Additional headers & params & body
headers: { "X-ADDITIONAL-HEADER": 'yes' },
params: { additionalParam1: 'yes' },
body: { additionalBody1: ['yes'] },
// And/or transform request callback
transformRequest: req => {
if (req.method === 'get') {
req.params.vf = "1"
}
return req;
},
// XSRF Token header name
xsrfHeaderName: "CSRF-TOKEN",
}
const maxFileSize = ref("500MB")
const features = [
...FEATURE_ALL_NAMES,
// Or remove the line above, specify the features want to include
// Like...
//FEATURES.LANGUAGE,
]
const selectedFiles = ref([]);
// an example how to show selected files, outside of vuefinder
// you can create a ref object and assign the items to it,
// then with a button click, you can get the selected items easily
const handleSelect = (selection) => {
selectedFiles.value = selection
}
const handleButton = () => {
console.log(selectedFiles.value)
}
</script>

<style>
Expand All @@ -22,7 +79,17 @@ body {
background: #eeeeee;
}
.wrapper {
width: 800px;
max-width: 800px;
margin: 100px auto;
}
.btn{
display: block;
margin: 20px auto;
padding: 10px 20px;
border: 1px solid #ccc;
border-radius: 5px;
background: #fff;
cursor: pointer;
outline: none;
}
</style>
13 changes: 13 additions & 0 deletions src/assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,19 @@
.vf-scrollbar::-webkit-scrollbar-corner {
@apply bg-transparent;
}


/* modal fade effect */
.vuefinder .fade-enter-active,
.vuefinder .fade-leave-active {
transition: opacity 0.2s ease;
}

.vuefinder .fade-enter-from,
.vuefinder .fade-leave-to {
opacity: 0;
}

}

@tailwind utilities;
Expand Down
6 changes: 4 additions & 2 deletions src/components/ActionMessage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="text-sm text-green-600 dark:text-green-600 transition-opacity duration-500 ease-out"
:class="[{ 'opacity-0': !shown }]">
<slot v-if="$slots.default"/>
<span v-else>Saved.</span>
<span v-else>{{ t('Saved.') }}</span>
</div>
</template>

Expand All @@ -16,6 +16,8 @@ export default {
setup(props, {emit, slots}) {
const emitter = inject('emitter');
const shown = ref(false);
const {t} = inject('i18n');
let timeout = null;
const handleEvent = () => {
Expand All @@ -35,7 +37,7 @@ export default {
});
return {
shown,
shown, t
};
},
};
Expand Down
8 changes: 7 additions & 1 deletion src/components/Breadcrumb.vue
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,18 @@ export default {
import {inject, nextTick, ref, watch} from 'vue';
import useDebouncedRef from '../composables/useDebouncedRef.js';
import {FEATURES} from "./features.js";
const emitter = inject('emitter');
const { getStore } = inject('storage');
const adapter = inject('adapter');
const dirname = ref(null);
const breadcrumb = ref([]);
const searchMode = ref(false);
const searchInput = ref(null);
/** @type {import('vue').Ref<String[]>} */
const features = inject('features');
const props = defineProps({
data: Object
});
Expand Down Expand Up @@ -135,6 +138,9 @@ emitter.on('vf-search-exit', () => {
});
const enterSearchMode = () => {
if (!features.value.includes(FEATURES.SEARCH)) {
return;
}
searchMode.value = true;
nextTick(() => searchInput.value.focus())
}
Expand Down
26 changes: 19 additions & 7 deletions src/components/ContextMenu.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<ul class="z-30 absolute text-xs bg-neutral-50 dark:bg-gray-800 text-gray-700 dark:text-gray-200 border border-neutral-300 dark:border-gray-600 shadow rounded select-none" ref="contextmenu" v-if="context.active" :style="context.positions">
<li class="px-2 py-1.5 cursor-pointer hover:bg-neutral-200 dark:hover:bg-gray-700"
v-for="(item) in context.items" :key="item.title" @click="run(item)">
v-for="(item) in filteredItems" :key="item.title" @click="run(item)">
<span class="px-1"></span>
<span>{{ item.title() }}</span>
</li>
Expand All @@ -15,16 +15,17 @@ export default {
</script>

<script setup>
import {inject, nextTick, reactive, ref} from 'vue';
import buildURLQuery from '../utils/buildURLQuery.js';
import {useApiUrl} from '../composables/useApiUrl.js';
import {computed, inject, nextTick, reactive, ref} from 'vue';
import {FEATURES} from "./features.js";
const emitter = inject('emitter');
const contextmenu = ref(null);
const {apiUrl} = useApiUrl();
const root = inject('root');
/** @type {import('../utils/ajax.js').Requester} */
const requester = inject('requester');
/** @type {import('vue').Ref<String[]>} */
const features = inject('features');
const props = defineProps({
current: Object
Expand All @@ -39,6 +40,10 @@ const context = reactive({
}
});
const filteredItems = computed(() => {
return context.items.filter(item => item.key == null || features.value.includes(item.key))
});
const selectedItems = ref([]);
emitter.on('vf-context-selected', (items) => {
Expand All @@ -48,12 +53,14 @@ const {t} = inject('i18n')
const menuItems = {
newfolder: {
key: FEATURES.NEW_FOLDER,
title: () => t('New Folder'),
action: () => {
emitter.emit('vf-modal-show', {type:'new-folder'});
},
},
delete: {
key: FEATURES.DELETE,
title: () => t('Delete'),
action: () => {
emitter.emit('vf-modal-show', {type:'delete', items: selectedItems});
Expand All @@ -66,6 +73,7 @@ const menuItems = {
},
},
preview: {
key: FEATURES.PREVIEW,
title: () => t('Preview'),
action: () => {
emitter.emit('vf-modal-show', {type:'preview', adapter:props.current.adapter, item: selectedItems.value[0]});
Expand All @@ -86,25 +94,29 @@ const menuItems = {
},
},
download: {
key: FEATURES.DOWNLOAD,
title: () => t('Download'),
action: () => {
const url = apiUrl.value + '?' + buildURLQuery({q:'download', adapter: props.current.adapter, path: selectedItems.value[0].path});
const url = requester.getDownloadUrl(props.current.adapter, selectedItems.value[0]);
emitter.emit('vf-download', url);
},
},
archive: {
key: FEATURES.ARCHIVE,
title: () => t('Archive'),
action: () => {
emitter.emit('vf-modal-show', {type:'archive', items: selectedItems});
},
},
unarchive: {
key: FEATURES.UNARCHIVE,
title: () => t('Unarchive'),
action: () => {
emitter.emit('vf-modal-show', {type:'unarchive', items: selectedItems});
},
},
rename: {
key: FEATURES.RENAME,
title: () => t('Rename'),
action: () => {
emitter.emit('vf-modal-show', {type:'rename', items: selectedItems});
Expand Down
5 changes: 3 additions & 2 deletions src/components/Explorer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
<svg v-if="item.type == 'dir'" xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 md:h-12 md:w-12 m-auto fill-sky-500 stroke-sky-500 dark:fill-slate-500 dark:stroke-slate-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 7v10a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-6l-2-2H5a2 2 0 00-2 2z" />
</svg>
<img class="lazy h-10 md:h-12 m-auto" v-else-if="(item.mime_type ?? '').startsWith('image')" :data-src="getImageUrl(adapter.value, item.path)" :alt="item.basename">
<img class="lazy h-10 md:h-12 m-auto" v-else-if="(item.mime_type ?? '').startsWith('image')" :data-src="requester.getPreviewUrl(adapter.value, item)" :alt="item.basename">
<svg v-else xmlns="http://www.w3.org/2000/svg" class="h-10 w-10 md:h-12 md:w-12 m-auto text-neutral-500" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="1">
<path stroke-linecap="round" stroke-linejoin="round" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z" />
</svg>
Expand All @@ -129,7 +129,6 @@ import DragSelect from 'dragselect';
import datetimestring from '../utils/datetimestring.js';
import VFSortIcon from './SortIcon.vue';
import VFToast from './Toast.vue';
import {getImageUrl} from '../utils/getImageUrl.js';
import LazyLoad from 'vanilla-lazyload';
import title_shorten from "../utils/title_shorten.js";
Expand All @@ -139,6 +138,8 @@ const props = defineProps({
search: Object
});
/** @type {import('../utils/ajax.js').Requester} */
const requester = inject('requester')
const emitter = inject('emitter');
const { setStore, getStore } = inject('storage');
const adapter = inject('adapter');
Expand Down
Loading

0 comments on commit 12bc814

Please sign in to comment.