Regression: Webdav File Picker search and fixed overflows (#15027)

pull/15067/head
Utkarsh Barsaiyan 6 years ago committed by Guilherme Gazzo
parent 6032af1f4e
commit 6bbc327a28
  1. 134
      app/webdav/client/webdavFilePicker.css
  2. 65
      app/webdav/client/webdavFilePicker.html
  3. 150
      app/webdav/client/webdavFilePicker.js

@ -46,33 +46,69 @@
}
.webdav-table-header {
margin-bottom: 12px;
display: flex;
flex-direction: row;
margin-bottom: 8px;
font-size: 16px;
color: #444444;
font-size: 0.9rem;
font-weight: 500;
line-height: 1rem;
align-items: center;
i {
color: #5b5b5b;
font-size: 16px;
}
.webdav-path-breadcrumb {
display: flex;
overflow-x: auto;
float: left;
overflow: hidden;
white-space: nowrap;
align-items: center;
flex-flow: row nowrap;
-ms-overflow-style: none;
list-style: none;
/* scrollbar-width: none; */
.webdav-breadcrumb-item {
display: block;
float: left;
&::-webkit-scrollbar {
display: none;
}
.webdav-breadcrumb-item {
padding: 3px 0;
&:last-child {
padding-right: 30px;
}
.webdav-breadcrumb-folder {
padding: 3px;
overflow: hidden;
max-width: 100px;
padding: 4px 3px;
cursor: pointer;
&:hover {
transition: all 0.2s ease-in-out;
white-space: nowrap;
text-overflow: ellipsis;
color: inherit;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
&:hover,
&:focus {
max-width: 300px;
border-radius: 5px;
background: #f1f3f4;
}
@ -80,15 +116,76 @@
}
}
.listOrGridMode {
float: right;
.webdav-header-spacer {
flex: 1;
}
.webdav-search {
position: relative;
&::before {
position: absolute;
z-index: 5;
top: 0;
left: -40px;
width: 40px;
height: 100%;
content: '';
pointer-events: none;
background: linear-gradient(to right, transparent, white);
}
.webdav-search-input {
width: 36px;
padding: 4px 6px 4px 32px;
cursor: pointer;
transition: width 0.3s ease-in-out;
border: none;
outline: none;
font-family: inherit;
font-size: 0.85rem;
font-weight: 400;
&:focus {
width: 200px;
padding-left: 40px;
cursor: auto;
}
}
.webdav-search-icon {
position: absolute;
z-index: 5;
top: 0;
left: 6px;
padding: 4px 3px;
cursor: pointer;
&:hover,
&:focus {
border-radius: 5px;
background: #f1f3f4;
}
}
}
.list-grid-mode {
margin-right: 5px;
padding: 3px;
padding: 4px 3px;
cursor: pointer;
&:hover {
&:hover,
&:focus {
border-radius: 5px;
background: #f1f3f4;
}
@ -130,7 +227,7 @@
font-size: 1rem;
}
& #webdav-go-back .rc-icon {
& .webdav-grid-back-icon .rc-icon {
cursor: pointer;
opacity: 1;
@ -156,7 +253,7 @@
font-weight: 500;
line-height: 1rem;
#webdav-go-back {
.webdav-grid-back-icon {
float: left;
.rc-icon {
@ -168,7 +265,7 @@
}
}
.select-sort {
.webdav-sort {
float: right;
height: auto;
@ -226,7 +323,8 @@
font-weight: 500;
line-height: 1rem;
&:hover {
&:hover,
&:focus {
border-radius: 5px;
background: #f1f3f4;
}

@ -1,23 +1,36 @@
<template name="webdavFilePicker">
<div class="webdav">
<div class="webdav-table-header">
<ul class="webdav-path-breadcrumb">
<li class="webdav-breadcrumb-item">
<span class="webdav-breadcrumb-folder" data-index="-1">
<div class="webdav-path-breadcrumb">
<div class="webdav-breadcrumb-item">
<button class="webdav-breadcrumb-folder js-webdav-breadcrumb-folder" aria-label="Home" data-index="-1">
<i class="icon-home"></i>
</span>
<i class="icon-angle-right"></i>
</li>
</button>
</div>
{{#each parentFolder in parentFolders}}
<li class="webdav-breadcrumb-item">
<span class="webdav-breadcrumb-folder" data-index="{{@index}}">
{{ parentFolder }}
</span>
<div class="webdav-breadcrumb-item">
<i class="icon-angle-right"></i>
</li>
<button class="webdav-breadcrumb-folder js-webdav-breadcrumb-folder" aria-label="{{ parentFolder }}" data-index="{{@index}}">
{{ parentFolder }}
</button>
</div>
{{/each}}
</ul>
<div class="listOrGridMode">
</div>
<div class="webdav-header-spacer"></div>
<div class="webdav-search">
<form class="webdav__search-form js-search-form" role="form">
<span class="webdav-search-icon js-webdav-search-icon rc-tooltip rc-tooltip--down" aria-label="Search">
<i class="icon-search"></i>
</span>
<input
type="text"
class="webdav-search-input js-webdav-search-input"
name="webdav-search-input"
placeholder="Search..."
autocomplete="off">
</form>
</div>
<div class="list-grid-mode js-list-grid-mode rc-tooltip rc-tooltip--down" aria-label="{{#if listMode}}Grid{{else}}List{{/if}} View">
<i class="icon-{{#if listMode}}th{{else}}list{{/if}}"></i>
</div>
</div>
@ -33,7 +46,7 @@
<tr>
<th class="webdav__file-icon">
<div class="table-fake-th">
<span id="webdav-go-back">
<span class="webdav-grid-back-icon js-webdav-grid-back-icon">
{{#if $neq webdavCurrentFolder '/'}}
{{>icon icon='back'}}
{{/if}}
@ -53,7 +66,7 @@
</thead>
<tbody>
{{#each webdavNodes}}
<tr class="webdav_{{this.type}}" data-name="{{this.basename}}">
<tr class="webdav_{{this.type}} js-webdav_{{this.type}}" data-name="{{this.basename}}">
<td>
<div class="table-file-avatar-wrapper">
{{#with iconType}}
@ -63,31 +76,29 @@
{{/with}}
</div>
</td>
<td>{{this.basename}}</td>
<td>{{getName this.basename}}</td>
<td>{{getSize}}</td>
<td class="table-column-date">{{getDate}}</td>
</tr>
{{else}}
<tr class="table-no-click">
<td colspan="4" class="center-cell">
There is nothing here!
</td>
<td colspan="4" class="center-cell">{{_ "Nothing_found"}}</td>
</tr>
{{/each}}
</tbody>
{{/table}}
{{else}}
<div class="webdav-grid-header">
<div id="webdav-go-back">
<div class="webdav-grid-back-icon js-webdav-grid-back-icon">
{{#if $neq webdavCurrentFolder '/'}}
{{>icon icon='back'}}
{{/if}}
</div>
<div class="rc-select select-sort">
<span class="webdav-sort-direction">
<div class="rc-select webdav-sort">
<span class="webdav-sort-direction js-webdav-sort-direction">
{{> icon icon=(sortIcon getSortBy)}}
</span>
<select id="webdav-select-sort" class="required rc-select__element">
<select class="js-webdav-select-sort required rc-select__element">
<option value="name" selected="{{sortBy 'name'}}" dir="auto">Name</option>
<option value="size" selected="{{sortBy 'size'}}" dir="auto">Size</option>
<option value="date" selected="{{sortBy 'date'}}" dir="auto">Date Modified</option>
@ -97,7 +108,7 @@
</div>
<div class="webdav-grid">
{{#each webdavNodes}}
<div class="webdav_{{this.type}} grid-item" data-name="{{this.basename}}">
<div class="webdav_{{this.type}} js-webdav_{{this.type}} grid-item" data-name="{{this.basename}}">
<div>
<div class="grid-file-avatar-wrapper">
{{#with iconType}}
@ -107,12 +118,10 @@
{{/with}}
</div>
</div>
<div>{{this.basename}}</div>
<div>{{getName this.basename}}</div>
</div>
{{else}}
<div class="grid-empty">
There is nothing here!
</div>
<div class="grid-empty">{{_ "Nothing_found"}}</div>
{{/each}}
</div>
{{/if}}

@ -5,28 +5,13 @@ import toastr from 'toastr';
import { Session } from 'meteor/session';
import { Handlebars } from 'meteor/ui';
import { ReactiveVar } from 'meteor/reactive-var';
import { ReactiveDict } from 'meteor/reactive-dict';
import { timeAgo } from '../../ui/client/views/app/helpers';
import { modal, call } from '../../ui-utils';
import { t } from '../../utils';
import { fileUploadHandler } from '../../file-upload';
Template.webdavFilePicker.rendered = async function() {
const { accountId } = this.data;
Session.set('webdavCurrentFolder', '/');
const response = await call('getWebdavFileList', accountId, '/');
if (!response.success) {
modal.close();
return toastr.error(t(response.message));
}
Session.set('webdavNodes', response.data);
this.isLoading.set(false);
};
Template.webdavFilePicker.destroyed = function() {
Session.set('webdavNodes', []);
};
function sortTable(data, sortBy, sortDirection) {
if (sortDirection === 'desc') {
if (sortBy === 'name') { data.sort((a, b) => b.basename.localeCompare(a.basename)); }
@ -40,21 +25,28 @@ function sortTable(data, sortBy, sortDirection) {
return data;
}
async function showWebdavFileList(directory) {
async function showWebdavFileList() {
const instance = Template.instance();
const { sortDirection, sortBy } = instance;
const { accountId } = instance.data;
const directory = instance.state.get('webdavCurrentFolder');
instance.isLoading.set(true);
Session.set('webdavCurrentFolder', directory);
Session.set('webdavNodes', []);
const response = await call('getWebdavFileList', accountId, directory);
instance.isLoading.set(false);
if (!response.success) {
modal.close();
return toastr.error(t(response.message));
instance.state.set({
webdavNodes: [],
});
try {
const response = await call('getWebdavFileList', accountId, directory);
if (!response.success) {
modal.close();
toastr.error(t(response.message));
}
instance.state.set({ unfilteredWebdavNodes: response.data });
$('.js-webdav-search-input').val('');
instance.searchText.set('');
} finally {
instance.isLoading.set(false);
}
const data = sortTable(response.data, sortBy.get(), sortDirection.get());
Session.set('webdavNodes', data);
}
Template.webdavFilePicker.helpers({
@ -99,6 +91,13 @@ Template.webdavFilePicker.helpers({
getSortBy() {
return Template.instance().sortBy.get();
},
getName(basename) {
const maxwidth = Template.instance().isListMode.get() ? 35 : 20;
if (basename.length < maxwidth) {
return basename;
}
return `${ basename.slice(0, maxwidth - 10) }\u2026${ basename.slice(-7) }`;
},
getSize() {
if (this.type === 'directory') { return ''; }
const bytes = this.size;
@ -126,42 +125,54 @@ Template.webdavFilePicker.helpers({
sortBy.set(type);
sortDirection.set('asc');
}
const data = sortTable(Session.get('webdavNodes'), sortBy.get(), sortDirection.get());
Session.set('webdavNodes', data);
};
},
parentFolders() {
const currentFolder = Session.get('webdavCurrentFolder');
const currentFolder = Template.instance().state.get('webdavCurrentFolder');
return currentFolder ? currentFolder.split('/').filter((s) => s) : [];
},
webdavNodes() {
return Session.get('webdavNodes');
return Template.instance().state.get('webdavNodes');
},
webdavCurrentFolder() {
return Session.get('webdavCurrentFolder');
return Template.instance().state.get('webdavCurrentFolder');
},
});
Template.webdavFilePicker.events({
'click .listOrGridMode'() {
'click .js-list-grid-mode'() {
const instance = Template.instance();
instance.isListMode.set(!instance.isListMode.get());
},
'click .webdav-sort-direction'() {
const { sortDirection, sortBy } = Template.instance();
'click .js-webdav-sort-direction'() {
const { sortDirection } = Template.instance();
sortDirection.set(sortDirection.get() === 'asc' ? 'desc' : 'asc');
const data = sortTable(Session.get('webdavNodes'), sortBy.get(), sortDirection.get());
Session.set('webdavNodes', data);
},
'change #webdav-select-sort'() {
const { sortDirection, sortBy } = Template.instance();
const newSortBy = $('#webdav-select-sort').val();
'change .js-webdav-select-sort'() {
const { sortBy } = Template.instance();
const newSortBy = $('.js-webdav-select-sort').val();
sortBy.set(newSortBy);
const data = sortTable(Session.get('webdavNodes'), sortBy.get(), sortDirection.get());
Session.set('webdavNodes', data);
},
async 'click #webdav-go-back'() {
let currentFolder = Session.get('webdavCurrentFolder');
'click .js-webdav-search-icon'() {
$('.js-webdav-search-input').focus();
},
'submit .js-search-form'(e) {
e.preventDefault();
e.stopPropagation();
},
'input .js-webdav-search-input': _.debounce((e, t) => {
t.searchText.set(e.currentTarget.value);
}, 200),
'blur .js-webdav-search-input'(e, t) {
_.delay(() => {
e.target.value = '';
t.searchText.set('');
}, 200);
},
async 'click .js-webdav-grid-back-icon'() {
const instance = Template.instance();
let currentFolder = instance.state.get('webdavCurrentFolder');
// determine parent directory to go back
let parentFolder = '/';
if (currentFolder && currentFolder !== '/') {
@ -170,14 +181,16 @@ Template.webdavFilePicker.events({
}
parentFolder = currentFolder.substr(0, currentFolder.lastIndexOf('/') + 1);
}
showWebdavFileList(parentFolder);
instance.state.set('webdavCurrentFolder', parentFolder);
},
async 'click .webdav_directory'() {
showWebdavFileList(this.filename);
async 'click .js-webdav_directory'() {
const instance = Template.instance();
instance.state.set('webdavCurrentFolder', this.filename);
},
async 'click .webdav-breadcrumb-folder'(event) {
async 'click .js-webdav-breadcrumb-folder'(event) {
const instance = Template.instance();
const index = $(event.target).data('index');
const currentFolder = Session.get('webdavCurrentFolder');
const currentFolder = instance.state.get('webdavCurrentFolder');
const parentFolders = currentFolder.split('/').filter((s) => s);
// determine parent directory to go to
let targetFolder = '/';
@ -185,9 +198,9 @@ Template.webdavFilePicker.events({
targetFolder += parentFolders[i];
targetFolder += '/';
}
showWebdavFileList(targetFolder);
instance.state.set('webdavCurrentFolder', targetFolder);
},
async 'click .webdav_file'() {
async 'click .js-webdav_file'() {
const roomId = Session.get('openedRoom');
const instance = Template.instance();
const { accountId } = instance.data;
@ -276,13 +289,12 @@ Template.webdavFilePicker.events({
});
}
Session.set('uploading', uploading);
return;
return Session.set('uploading', uploading);
}
if (file) {
Meteor.call('sendFileMessage', roomId, storage, file, () => {
Meteor.setTimeout(() => {
setTimeout(() => {
const uploading = Session.get('uploading');
if (uploading !== null) {
const item = _.findWhere(uploading, {
@ -298,9 +310,39 @@ Template.webdavFilePicker.events({
},
});
Template.webdavFilePicker.onRendered(async function() {
this.autorun(() => {
showWebdavFileList();
});
this.autorun(() => {
const { sortDirection, sortBy } = Template.instance();
const data = sortTable(this.state.get('webdavNodes'), sortBy.get(), sortDirection.get());
this.state.set('webdavNodes', data);
});
this.autorun(() => {
const loading = this.isLoading.get();
if (loading) {
return;
}
const input = this.searchText.get();
const regex = new RegExp(`\\b${ input }`, 'i');
const data = this.state.get('unfilteredWebdavNodes').filter(({ basename }) => basename.match(regex));
this.state.set('webdavNodes', data);
});
});
Template.webdavFilePicker.onCreated(function() {
this.state = new ReactiveDict({
webdavCurrentFolder: '/',
webdavNodes: [],
unfilteredWebdavNodes: [],
});
this.isLoading = new ReactiveVar(true);
this.isListMode = new ReactiveVar(true);
this.sortBy = new ReactiveVar('name');
this.sortDirection = new ReactiveVar('asc');
this.searchText = new ReactiveVar('');
});

Loading…
Cancel
Save