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 { .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 { i {
color: #5b5b5b; color: #5b5b5b;
font-size: 16px;
} }
.webdav-path-breadcrumb { .webdav-path-breadcrumb {
display: flex;
overflow-x: auto;
float: left; white-space: nowrap;
overflow: hidden; align-items: center;
flex-flow: row nowrap;
-ms-overflow-style: none;
list-style: none; /* scrollbar-width: none; */
.webdav-breadcrumb-item { &::-webkit-scrollbar {
display: block; display: none;
float: left; }
.webdav-breadcrumb-item {
padding: 3px 0; padding: 3px 0;
&:last-child {
padding-right: 30px;
}
.webdav-breadcrumb-folder { .webdav-breadcrumb-folder {
padding: 3px; overflow: hidden;
max-width: 100px;
padding: 4px 3px;
cursor: pointer; 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; border-radius: 5px;
background: #f1f3f4; background: #f1f3f4;
} }
@ -80,15 +116,76 @@
} }
} }
.listOrGridMode { .webdav-header-spacer {
float: right; 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; margin-right: 5px;
padding: 3px; padding: 4px 3px;
cursor: pointer; cursor: pointer;
&:hover { &:hover,
&:focus {
border-radius: 5px; border-radius: 5px;
background: #f1f3f4; background: #f1f3f4;
} }
@ -130,7 +227,7 @@
font-size: 1rem; font-size: 1rem;
} }
& #webdav-go-back .rc-icon { & .webdav-grid-back-icon .rc-icon {
cursor: pointer; cursor: pointer;
opacity: 1; opacity: 1;
@ -156,7 +253,7 @@
font-weight: 500; font-weight: 500;
line-height: 1rem; line-height: 1rem;
#webdav-go-back { .webdav-grid-back-icon {
float: left; float: left;
.rc-icon { .rc-icon {
@ -168,7 +265,7 @@
} }
} }
.select-sort { .webdav-sort {
float: right; float: right;
height: auto; height: auto;
@ -226,7 +323,8 @@
font-weight: 500; font-weight: 500;
line-height: 1rem; line-height: 1rem;
&:hover { &:hover,
&:focus {
border-radius: 5px; border-radius: 5px;
background: #f1f3f4; background: #f1f3f4;
} }

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

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

Loading…
Cancel
Save