mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 06:30:59 +01:00
Switch VfsSelectDialog to new SearchMixin
This commit is contained in:
parent
b0e8666ecb
commit
a8389d4286
@ -13,13 +13,13 @@ export default css`
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.vfs_select__listbox {
|
||||
.search__results {
|
||||
flex: 2 1 auto;
|
||||
min-height: 15em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.vfs_select__listbox .vfs_select__empty {
|
||||
.search__results .search__empty {
|
||||
height: 50%;
|
||||
min-height: 5em;
|
||||
min-width: 20em;
|
||||
@ -30,23 +30,24 @@ export default css`
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.search__results .search__empty et2-image {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.vfs_select__file_row {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.vfs_select__listbox .vfs_select__loading {
|
||||
.search__results .search__loading {
|
||||
text-align: center;
|
||||
line-height: 15em; // 3 * listbox min height
|
||||
}
|
||||
|
||||
.vfs_select__listbox sl-spinner {
|
||||
.search__results sl-spinner {
|
||||
font-size: 4rem;
|
||||
}
|
||||
.vfs_select__listbox .vfs_select__empty et2-image {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.vfs_select__listbox .more {
|
||||
.search__results .search__more {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -14,9 +14,8 @@ import styles from "./Et2VfsSelect.styles";
|
||||
import {property} from "lit/decorators/property.js";
|
||||
import {state} from "lit/decorators/state.js";
|
||||
import {ifDefined} from "lit/directives/if-defined.js";
|
||||
import {classMap} from "lit/directives/class-map.js";
|
||||
import {repeat} from "lit/directives/repeat.js";
|
||||
import {until} from "lit/directives/until.js";
|
||||
import {SearchMixinInterface} from "../Et2Select/SearchMixin";
|
||||
import {SelectOption} from "../Et2Select/FindSelectOptions";
|
||||
import {DialogButton, Et2Dialog} from "../Et2Dialog/Et2Dialog";
|
||||
import {HasSlotController} from "../Et2Widget/slot";
|
||||
@ -24,6 +23,7 @@ import {egw, IegwAppLocal} from "../../jsapi/egw_global";
|
||||
import {Et2Select} from "../Et2Select/Et2Select";
|
||||
import {Et2VfsSelectRow} from "./Et2VfsSelectRow";
|
||||
import {Et2VfsPath} from "./Et2VfsPath";
|
||||
import {SearchMixin, SearchResult, SearchResultElement, SearchResultsInterface} from "../Et2Widget/SearchMixin";
|
||||
|
||||
/**
|
||||
* @summary Select files (including directories) from the VFS.
|
||||
@ -55,7 +55,10 @@ import {Et2VfsPath} from "./Et2VfsPath";
|
||||
*
|
||||
*/
|
||||
|
||||
export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements SearchMixinInterface
|
||||
type Constructor<T = {}> = new (...args : any[]) => T;
|
||||
|
||||
export class Et2VfsSelectDialog
|
||||
extends SearchMixin<Constructor<any> & typeof LitElement, FileInfo, FileResultsInterface>(Et2InputWidget(LitElement))
|
||||
{
|
||||
static get styles()
|
||||
{
|
||||
@ -113,9 +116,8 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
/** The select's help text. If you need to display HTML, use the `help-text` slot instead. */
|
||||
@property({attribute: 'help-text'}) helpText = '';
|
||||
|
||||
@state() searching = false;
|
||||
@state() open : boolean = false;
|
||||
@state() currentFile : Et2VfsSelectRow;
|
||||
@state() currentResult : Et2VfsSelectRow;
|
||||
@state() selectedFiles : Et2VfsSelectRow[] = [];
|
||||
@state() _pathWritable : boolean = false;
|
||||
|
||||
@ -123,25 +125,14 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
// SearchMixinInterface //
|
||||
@property() searchUrl : string = "EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelectFiles";
|
||||
|
||||
/** Additional options passed to server search */
|
||||
@property({type: Object}) searchOptions : object = {};
|
||||
|
||||
search : boolean = true;
|
||||
allowFreeEntries : boolean = false;
|
||||
|
||||
// End SearchMixinInterface //
|
||||
protected _searchTimeout : number;
|
||||
protected _searchPromise : Promise<FileInfo[]> = Promise.resolve([]);
|
||||
private static SEARCH_TIMEOUT : number = 500;
|
||||
private _total_result_count : number = 0;
|
||||
|
||||
// Still need some server-side info
|
||||
protected _serverContent : Promise<any> = Promise.resolve({});
|
||||
private static SERVER_URL = "EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_vfsSelect_content";
|
||||
|
||||
protected readonly hasSlotController = new HasSlotController(this, 'help-text', 'toolbar', 'footer');
|
||||
protected readonly hasSlotController = new HasSlotController(<LitElement><unknown>this, 'help-text', 'toolbar', 'footer');
|
||||
|
||||
protected _fileList : FileInfo[] = [];
|
||||
// @ts-ignore different types
|
||||
protected _appList : SelectOption[] = this.egw().link_app_list("query") ?? [];
|
||||
|
||||
@ -152,12 +143,12 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
|
||||
get _fileNodes() : Et2VfsSelectRow[] { return Array.from(this.shadowRoot.querySelectorAll("et2-vfs-select-row"));}
|
||||
|
||||
get _resultNodes() : (HTMLElement & SearchResultElement)[] { return <(HTMLElement & SearchResultElement)[]><unknown>this._fileNodes;}
|
||||
|
||||
get _searchNode() : HTMLInputElement { return this.shadowRoot.querySelector("#search");}
|
||||
|
||||
get _pathNode() : Et2VfsPath { return this.shadowRoot.querySelector("#path");}
|
||||
|
||||
get _listNode() : HTMLElement { return this.shadowRoot.querySelector("#listbox");}
|
||||
|
||||
get _mimeNode() : Et2Select { return this.shadowRoot.querySelector("#mimeFilter");}
|
||||
|
||||
/*
|
||||
@ -187,7 +178,6 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
|
||||
this.handleClose = this.handleClose.bind(this);
|
||||
this.handleCreateDirectory = this.handleCreateDirectory.bind(this);
|
||||
this.handleSearchKeyDown = this.handleSearchKeyDown.bind(this);
|
||||
}
|
||||
|
||||
transformAttributes(attr)
|
||||
@ -257,7 +247,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
const oldValue = this.path;
|
||||
this._pathNode.value = this.path = path;
|
||||
this.requestUpdate("path", oldValue);
|
||||
this.currentFile = null;
|
||||
this.currentResult = null;
|
||||
|
||||
return this._searchPromise;
|
||||
}
|
||||
@ -283,7 +273,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
*/
|
||||
public fileInfo(_path)
|
||||
{
|
||||
return this._fileList.find(f => f.path == _path);
|
||||
return this._searchResults.find(f => f.path == _path);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -292,7 +282,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
public show()
|
||||
{
|
||||
this.open = true;
|
||||
if(this.path && this._fileList.length == 0)
|
||||
if(this.path && this._searchResults.length == 0)
|
||||
{
|
||||
this.startSearch();
|
||||
}
|
||||
@ -327,31 +317,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
return value;
|
||||
}
|
||||
|
||||
startSearch() : Promise<void>
|
||||
{
|
||||
// Stop timeout timer
|
||||
clearTimeout(this._searchTimeout);
|
||||
|
||||
this._total_result_count = 0;
|
||||
this.searching = true;
|
||||
this.requestUpdate("searching");
|
||||
|
||||
// Start the searches
|
||||
this._searchPromise = this.remoteSearch(this._searchNode?.value ?? "", this.searchOptions);
|
||||
return this._searchPromise.then(async() =>
|
||||
{
|
||||
this.searching = false;
|
||||
this.requestUpdate("searching", true);
|
||||
});
|
||||
}
|
||||
|
||||
localSearch(search : string, options : object) : Promise<FileInfo[]>
|
||||
{
|
||||
// No local search
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
remoteSearch(search : string, options : object) : Promise<FileInfo[]>
|
||||
remoteSearch<FileInfo>(search : string, options : object) : Promise<FileInfo[]>
|
||||
{
|
||||
// Include a limit, even if options don't, to avoid massive lists breaking the UI
|
||||
let sendOptions = {
|
||||
@ -360,14 +326,12 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
num_rows: 100,
|
||||
...options
|
||||
}
|
||||
return this.egw().request(this.egw().link(this.egw().ajaxUrl(this.egw().decodePath(this.searchUrl))), [search, sendOptions]).then((results) =>
|
||||
{
|
||||
return this.processRemoteResults(results);
|
||||
});
|
||||
return super.remoteSearch(search, sendOptions);
|
||||
}
|
||||
|
||||
processRemoteResults(results) : FileInfo[]
|
||||
processRemoteResults<FileInfo>(results) : FileInfo[]
|
||||
{
|
||||
const result = super.processRemoteResults(results);
|
||||
if(typeof results.path === "string")
|
||||
{
|
||||
// Something like a redirect or link followed - server is sending us a "corrected" path
|
||||
@ -378,22 +342,11 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
this._pathWritable = results.writable;
|
||||
this.requestUpdate("_pathWritable");
|
||||
}
|
||||
if(typeof results.total !== "undefined")
|
||||
{
|
||||
this._total_result_count = results.total;
|
||||
}
|
||||
|
||||
this.helpText = results?.message ?? "";
|
||||
this._fileList = results?.files ?? [];
|
||||
|
||||
return this._fileList;
|
||||
return result;
|
||||
}
|
||||
|
||||
searchMatch(search : string, options : object, item : LitElement) : boolean
|
||||
{
|
||||
// No local matching
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject application specific egw object with loaded translations into the dialog
|
||||
*
|
||||
@ -528,80 +481,22 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
this.selectionChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current file, which is the option the user is currently interacting with (e.g. via keyboard).
|
||||
* Only one file may be "current" at a time. This is unrelated to the value.
|
||||
*/
|
||||
private setCurrentFile(file : Et2VfsSelectRow | null)
|
||||
{
|
||||
// Clear selection
|
||||
this._fileNodes.forEach(el =>
|
||||
{
|
||||
el.current = false;
|
||||
el.tabIndex = -1;
|
||||
el.requestUpdate("current");
|
||||
});
|
||||
|
||||
// Select the target option
|
||||
if(file)
|
||||
{
|
||||
this.currentFile = file;
|
||||
file.current = true;
|
||||
file.tabIndex = 0;
|
||||
file.focus();
|
||||
file.requestUpdate("current");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles a file's selected state
|
||||
*/
|
||||
private toggleFileSelection(file : Et2VfsSelectRow, force? : boolean)
|
||||
{
|
||||
if(force === true || force === false)
|
||||
{
|
||||
file.selected = force;
|
||||
}
|
||||
else
|
||||
{
|
||||
file.selected = !file.selected;
|
||||
}
|
||||
|
||||
file.requestUpdate("selected");
|
||||
this.selectionChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be called whenever the selection changes. It will update the selected file cache, the current
|
||||
* value, and the display value
|
||||
*/
|
||||
private selectionChanged()
|
||||
protected searchResultSelected()
|
||||
{
|
||||
// Update selected files cache
|
||||
this.selectedFiles = this._fileNodes.filter(el => el.selected);
|
||||
super.searchResultSelected();
|
||||
|
||||
// Update the value
|
||||
if(this.multiple)
|
||||
{
|
||||
this.value = this.selectedFiles.map(el => el.value.path);
|
||||
|
||||
// TODO - show how many are selected?
|
||||
/*
|
||||
if(this.value.length === 0)
|
||||
{
|
||||
// When no items are selected, keep the value empty so the placeholder shows
|
||||
this.displayLabel = '';
|
||||
this.value = this.selectedResults.map(el => el.value.path);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.displayLabel = this.localize.term('numOptionsSelected', this.selectedFiles.length);
|
||||
}
|
||||
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
this.value = [this.selectedFiles[0]?.value.path] ?? [];
|
||||
this.value = [this.selectedResults[0]?.value.path] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
@ -638,7 +533,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
|
||||
if(file && !file.disabled)
|
||||
{
|
||||
this.currentFile = file;
|
||||
this.currentResult = file;
|
||||
|
||||
// Can't select a directory normally, can't select anything in "saveas"
|
||||
if(file.value.isDir && this.mode != "select-dir" || this.mode == "saveas")
|
||||
@ -647,7 +542,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
}
|
||||
if(this.multiple)
|
||||
{
|
||||
this.toggleFileSelection(file);
|
||||
this.toggleResultSelection(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -666,7 +561,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
|
||||
if(file.value.isDir)
|
||||
{
|
||||
this.toggleFileSelection(file, false);
|
||||
this.toggleResultSelection(file, false);
|
||||
const oldPath = this.path;
|
||||
this.setPath(file.value.path);
|
||||
}
|
||||
@ -698,7 +593,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
if(["ArrowUp", "ArrowDown", "Home", "End"].includes(event.key))
|
||||
{
|
||||
const files = this._fileNodes;
|
||||
const currentIndex = files.indexOf(this.currentFile);
|
||||
const currentIndex = files.indexOf(this.currentResult);
|
||||
let newIndex = Math.max(0, currentIndex);
|
||||
|
||||
// Prevent scrolling
|
||||
@ -729,16 +624,16 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
newIndex = files.length - 1;
|
||||
}
|
||||
|
||||
this.setCurrentFile(files[newIndex]);
|
||||
this.setCurrentResult(files[newIndex]);
|
||||
}
|
||||
else if([" "].includes(event.key) && this.currentFile)
|
||||
else if([" "].includes(event.key) && this.currentResult)
|
||||
{
|
||||
// Prevent scrolling
|
||||
event.preventDefault();
|
||||
|
||||
return this.handleFileClick(event);
|
||||
}
|
||||
else if(["Enter"].includes(event.key) && this.currentFile && !this.currentFile.disabled)
|
||||
else if(["Enter"].includes(event.key) && this.currentResult && !this.currentResult.disabled)
|
||||
{
|
||||
return this.handleFileDoubleClick(event);
|
||||
}
|
||||
@ -753,22 +648,19 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
clearTimeout(this._searchTimeout);
|
||||
|
||||
// Up / Down navigates options
|
||||
if(['ArrowDown', 'ArrowUp'].includes(event.key) && this._fileList.length)
|
||||
if(['ArrowDown', 'ArrowUp'].includes(event.key) && this._searchResults.length)
|
||||
{
|
||||
event.stopPropagation();
|
||||
this.setCurrentFile(this._fileNodes[0]);
|
||||
return;
|
||||
return super.handleSearchKeyDown(event);
|
||||
}
|
||||
// Start search immediately
|
||||
else if(event.key == "Enter")
|
||||
{
|
||||
event.preventDefault();
|
||||
this.startSearch();
|
||||
return;
|
||||
return super.handleSearchKeyDown(event);
|
||||
}
|
||||
else if(event.key == "Escape")
|
||||
{
|
||||
event.stopPropagation();
|
||||
super.handleSearchKeyDown(event);
|
||||
|
||||
event.preventDefault();
|
||||
this.value = [];
|
||||
this.hide();
|
||||
@ -828,18 +720,15 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
`;
|
||||
}
|
||||
|
||||
protected filesTemplate()
|
||||
protected resultTemplate(file : FileInfo, index)
|
||||
{
|
||||
const empty = this._fileList.length == 0;
|
||||
const classes = file.class ? Object.fromEntries((file.class).split(" ").map(k => [k, true])) : {};
|
||||
|
||||
const promise = this._searchPromise.then(() =>
|
||||
{
|
||||
return html`
|
||||
${empty ? this.noFilesTemplate() : html`
|
||||
${repeat(this._fileList, (file) => file.path, (file, index) =>
|
||||
{
|
||||
return html`
|
||||
<et2-vfs-select-row
|
||||
class=${classMap({
|
||||
...classes
|
||||
})}
|
||||
?disabled=${file.disabled || this.mode == "select-dir" && !file.isDir}
|
||||
.selected=${this.value.includes(file.path)}
|
||||
.value=${file}
|
||||
@ -847,44 +736,16 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
@dblclick=${this.handleFileDoubleClick}
|
||||
></et2-vfs-select-row>`;
|
||||
}
|
||||
)}
|
||||
${until(this.moreResultsTemplate(), nothing)}
|
||||
`
|
||||
}`;
|
||||
});
|
||||
return html`
|
||||
${until(promise, html`
|
||||
<div class="vfs_select__loading">
|
||||
<sl-spinner></sl-spinner>
|
||||
</div>`)}`;
|
||||
}
|
||||
|
||||
protected noFilesTemplate() : TemplateResult
|
||||
protected noResultsTemplate() : TemplateResult
|
||||
{
|
||||
return html`
|
||||
<div class="vfs_select__empty">
|
||||
<div class="search__empty vfs_select__empty">
|
||||
<et2-image src="filemanager"></et2-image>
|
||||
${this.egw().lang("no files in this directory.")}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
protected async moreResultsTemplate()
|
||||
{
|
||||
if(this._total_result_count <= 0 || !this._searchPromise || !this._listNode)
|
||||
{
|
||||
return nothing;
|
||||
}
|
||||
return this._searchPromise.then(() =>
|
||||
{
|
||||
const moreCount = this._total_result_count - this._fileList.length;
|
||||
const more = this.egw().lang("%1 more...", moreCount);
|
||||
|
||||
return html`${moreCount > 0 ?
|
||||
html`
|
||||
<div class="more">${more}</div>` : nothing}`;
|
||||
});
|
||||
}
|
||||
|
||||
protected mimeOptionsTemplate()
|
||||
{
|
||||
return html``;
|
||||
@ -970,18 +831,7 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
@change=${() => {this.setPath(this._pathNode.value)}}
|
||||
></et2-vfs-path>
|
||||
</div>
|
||||
<div
|
||||
id="listbox"
|
||||
role="listbox"
|
||||
aria-expanded=${this.open ? 'true' : 'false'}
|
||||
aria-multiselectable=${this.multiple ? 'true' : "false"}
|
||||
aria-labelledby="title"
|
||||
part="listbox"
|
||||
class="vfs_select__listbox"
|
||||
tabindex="-1"
|
||||
>
|
||||
${this.filesTemplate()}
|
||||
</div>
|
||||
${this.searchResultsTemplate()}
|
||||
<sl-visually-hidden>
|
||||
<et2-label for="mimeFilter">${this.egw().lang("mime filter")}</et2-label>
|
||||
</sl-visually-hidden>
|
||||
@ -1018,14 +868,23 @@ export class Et2VfsSelectDialog extends Et2InputWidget(LitElement) implements Se
|
||||
|
||||
customElements.define("et2-vfs-select-dialog", Et2VfsSelectDialog);
|
||||
|
||||
export interface FileInfo
|
||||
export interface FileInfo extends SearchResult
|
||||
{
|
||||
name : string,
|
||||
mime : string,
|
||||
isDir : boolean,
|
||||
// Full VFS path
|
||||
path? : string,
|
||||
// We want to show it, but not act with it. File is disabled for the UI
|
||||
disabled? : boolean,
|
||||
// Direct download link
|
||||
downloadUrl? : string
|
||||
}
|
||||
|
||||
/**
|
||||
* We expect the server to respond with file data in this format
|
||||
*/
|
||||
interface FileResultsInterface extends SearchResultsInterface<FileInfo>
|
||||
{
|
||||
// Something like a redirect or link followed - server is sending us a "corrected" path
|
||||
path? : string,
|
||||
// The current directory is not writable
|
||||
writable? : boolean
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ type Constructor<T = {}> = new (...args : any[]) => T;
|
||||
*
|
||||
* ## Override:
|
||||
* These methods must be overridden:
|
||||
* selectionChanged() - Called when the user has selected a search result. You need to call super.selectionChanged(), then
|
||||
* searchResultSelected() - Called when the user has selected a search result. You need to call super.selectionChanged(), then
|
||||
* update your value from `this.selectedResults`.
|
||||
*
|
||||
* ```ts
|
||||
@ -239,8 +239,8 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
|
||||
|
||||
// Start the searches
|
||||
this._searchPromise = Promise.all([
|
||||
this.localSearch(this._searchNode.value, this.searchOptions),
|
||||
this.remoteSearch(this._searchNode.value, this.searchOptions)
|
||||
this.localSearch(this._searchNode?.value ?? "", this.searchOptions),
|
||||
this.remoteSearch(this._searchNode?.value ?? "", this.searchOptions)
|
||||
]).then(async() =>
|
||||
{
|
||||
this.searching = false;
|
||||
@ -382,6 +382,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
|
||||
{
|
||||
el.current = false;
|
||||
el.tabIndex = -1;
|
||||
el.requestUpdate("current");
|
||||
});
|
||||
|
||||
// Select the target option
|
||||
@ -391,6 +392,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
|
||||
result.current = true;
|
||||
result.tabIndex = 0;
|
||||
result.focus();
|
||||
result.requestUpdate("current");
|
||||
}
|
||||
}
|
||||
|
||||
@ -684,7 +686,7 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
|
||||
}
|
||||
return this._searchPromise.then(() =>
|
||||
{
|
||||
const moreCount = this._totalResults - this._resultNodes.length;
|
||||
const moreCount = this._totalResults - this._searchResults.length;
|
||||
const more = this.egw().lang("%1 more...", moreCount);
|
||||
|
||||
return html`${moreCount > 0 ?
|
||||
@ -694,5 +696,5 @@ export const SearchMixin = <T extends Constructor<Et2InputWidgetInterface &
|
||||
}
|
||||
|
||||
};
|
||||
return SearchMixinClass as unknown as Constructor<SearchMixinInterface<DataType, Results>> & T & LitElement;
|
||||
return SearchMixinClass as unknown as Constructor<SearchMixinInterface<DataType, Results>> & LitElement & T;
|
||||
}
|
@ -614,7 +614,7 @@ class Vfs extends File
|
||||
*/
|
||||
public static function ajax_vfsSelectFiles($search, $content)
|
||||
{
|
||||
$response = [];
|
||||
$response = ['results' => []];
|
||||
$content['path'] = $content['path'] ?? '~';
|
||||
if($content['path'] == '~')
|
||||
{
|
||||
@ -651,7 +651,7 @@ class Vfs extends File
|
||||
$mime = $path['mime'] ?? Api\Vfs::mime_content_type($path);
|
||||
$download = $path['download_url'] ?? Api\Vfs::download_url($path);
|
||||
|
||||
$response['files'][] = array(
|
||||
$response['results'][] = array(
|
||||
'name' => $name,
|
||||
'path' => $path,
|
||||
'mime' => $mime,
|
||||
|
Loading…
Reference in New Issue
Block a user