mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-29 10:09:10 +01:00
Work on LinkEntry
This commit is contained in:
parent
c572fa637d
commit
9b0e1b9206
@ -13,6 +13,7 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
||||
:host {
|
||||
--icon-width: 20px;
|
||||
display: inline-block;
|
||||
min-width: 64px;
|
||||
}
|
||||
:host([app_icons]) {
|
||||
max-width: 75px;
|
||||
@ -130,6 +131,22 @@ export class Et2LinkAppSelect extends SlotMixin(Et2Select)
|
||||
}
|
||||
}
|
||||
|
||||
set application_list(app_list : string[])
|
||||
{
|
||||
let oldValue = this.__application_list;
|
||||
if(typeof app_list == "string")
|
||||
{
|
||||
app_list = (<string>app_list).split(",");
|
||||
}
|
||||
this.__application_list = app_list;
|
||||
this.requestUpdate("application_list", oldValue);
|
||||
}
|
||||
|
||||
get application_list() : string[]
|
||||
{
|
||||
return this.__application_list;
|
||||
}
|
||||
|
||||
private _handleChange(e)
|
||||
{
|
||||
// Set icon
|
||||
|
142
api/js/etemplate/Et2Link/Et2LinkEntry.ts
Normal file
142
api/js/etemplate/Et2Link/Et2LinkEntry.ts
Normal file
@ -0,0 +1,142 @@
|
||||
import {css, html, LitElement, SlotMixin} from "@lion/core";
|
||||
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
||||
import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
|
||||
import {FormControlMixin, ValidateMixin} from "@lion/form-core";
|
||||
import {Et2LinkSearch} from "./Et2LinkSearch";
|
||||
|
||||
/**
|
||||
* EGroupware eTemplate2 - Search & select link entry WebComponent
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @link https://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
*/
|
||||
export class Et2LinkEntry extends Et2InputWidget(FormControlMixin(ValidateMixin(SlotMixin(LitElement))))
|
||||
{
|
||||
static get styles()
|
||||
{
|
||||
return [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`
|
||||
];
|
||||
}
|
||||
|
||||
static get properties()
|
||||
{
|
||||
return {
|
||||
...super.properties,
|
||||
|
||||
value: {type: Object},
|
||||
/**
|
||||
* Limit to just this application - hides app selection
|
||||
*/
|
||||
only_app: {type: String},
|
||||
/**
|
||||
* Limit to the listed applications (comma seperated)
|
||||
*/
|
||||
application_list: {type: String},
|
||||
/**
|
||||
* Show just application icons instead of names
|
||||
*/
|
||||
app_icons: {type: Boolean},
|
||||
/**
|
||||
* Callback before query to server.
|
||||
* It will be passed the request & et2_link_entry objects. Must return true, or false to abort query.
|
||||
*/
|
||||
query: {type: Function},
|
||||
/**
|
||||
* Callback when user selects an option. Must return true, or false to abort normal action.
|
||||
*/
|
||||
select: {type: Function}
|
||||
}
|
||||
}
|
||||
|
||||
get slots()
|
||||
{
|
||||
return {
|
||||
...super.slots,
|
||||
app: () =>
|
||||
{
|
||||
const app = document.createElement("et2-link-apps")
|
||||
return app;
|
||||
},
|
||||
select: () =>
|
||||
{
|
||||
const select = <Et2LinkSearch><unknown>document.createElement("et2-link-search");
|
||||
return select;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback()
|
||||
{
|
||||
super.connectedCallback();
|
||||
|
||||
this._handleAppChange = this._handleAppChange.bind(this);
|
||||
}
|
||||
|
||||
set only_app(app)
|
||||
{
|
||||
this._appNode.only_app = app;
|
||||
this._searchNode.app = app;
|
||||
}
|
||||
|
||||
get only_app()
|
||||
{
|
||||
return this._appNode.only_app;
|
||||
}
|
||||
|
||||
get _appNode() : Et2LinkAppSelect
|
||||
{
|
||||
return this.querySelector("[slot='app']");
|
||||
}
|
||||
|
||||
get _searchNode() : Et2LinkSearch
|
||||
{
|
||||
return this.querySelector("[slot='select']");
|
||||
}
|
||||
|
||||
|
||||
protected _bindListeners()
|
||||
{
|
||||
this._appNode.addEventListener("change", this._handleAppChange);
|
||||
}
|
||||
|
||||
protected _unbindListeners()
|
||||
{
|
||||
this._appNode.removeEventListener("change", this._handleAppChange);
|
||||
}
|
||||
|
||||
protected _handleAppChange(event)
|
||||
{
|
||||
this._searchNode.app = this._appNode.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {TemplateResult}
|
||||
* @protected
|
||||
*/
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
_inputGroupInputTemplate()
|
||||
{
|
||||
return html`
|
||||
<div class="input-group__input">
|
||||
<slot name="app"></slot>
|
||||
<slot name="select"></slot>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-link-entry", Et2LinkEntry);
|
72
api/js/etemplate/Et2Link/Et2LinkSearch.ts
Normal file
72
api/js/etemplate/Et2Link/Et2LinkSearch.ts
Normal file
@ -0,0 +1,72 @@
|
||||
/**
|
||||
* EGroupware eTemplate2 - Search & select link entry WebComponent
|
||||
*
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @link https://www.egroupware.org
|
||||
* @author Nathan Gray
|
||||
*/
|
||||
|
||||
import {css} from "@lion/core";
|
||||
import {Et2Select} from "../Et2Select/Et2Select";
|
||||
import {Et2LinkAppSelect} from "./Et2LinkAppSelect";
|
||||
|
||||
export class Et2LinkSearch extends Et2Select
|
||||
{
|
||||
static get styles()
|
||||
{
|
||||
return [
|
||||
...super.styles,
|
||||
css`
|
||||
:host {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
min-width: 200px;
|
||||
}
|
||||
::part(icon), .select__icon {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
static get properties()
|
||||
{
|
||||
return {
|
||||
...super.properties,
|
||||
app: {type: String, reflect: true}
|
||||
}
|
||||
}
|
||||
|
||||
constructor()
|
||||
{
|
||||
super();
|
||||
this.search = true;
|
||||
this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_search";
|
||||
}
|
||||
|
||||
get _appNode() : Et2LinkAppSelect
|
||||
{
|
||||
return this.parentNode.querySelector("et2-link-apps");
|
||||
}
|
||||
|
||||
protected remoteQuery(search : string, options : object)
|
||||
{
|
||||
let request = this.egw().json(this.searchUrl, [this._appNode.value, '', search, options]);
|
||||
if(this.query && typeof this.query == "function")
|
||||
{
|
||||
if(!this.query(request, this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
request.sendRequest().then((result) =>
|
||||
{
|
||||
this.processRemoteResults(result);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore TypeScript is not recognizing that this widget is a LitElement
|
||||
customElements.define("et2-link-search", Et2LinkSearch);
|
@ -31,6 +31,12 @@ export declare class SearchMixinInterface
|
||||
*/
|
||||
allowFreeEntries : boolean;
|
||||
|
||||
/**
|
||||
* Additional search options passed to the search functions
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
searchOptions : object;
|
||||
|
||||
/**
|
||||
* Start the search process
|
||||
@ -40,18 +46,18 @@ export declare class SearchMixinInterface
|
||||
/**
|
||||
* Search local options
|
||||
*/
|
||||
localSearch(search : string) : Promise<void>
|
||||
localSearch(search : string, options : object) : Promise<void>
|
||||
|
||||
/**
|
||||
* Search remote options.
|
||||
* If searchUrl is not set, it will return very quickly with no results
|
||||
*/
|
||||
remoteSearch(search : string) : Promise<void>
|
||||
remoteSearch(search : string, options : object) : Promise<void>
|
||||
|
||||
/**
|
||||
* Check a [local] item to see if it matches
|
||||
*/
|
||||
searchMatch(search : string, item : LitElement) : boolean
|
||||
searchMatch(search : string, options : object, item : LitElement) : boolean
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +78,9 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
|
||||
searchUrl: {type: String},
|
||||
|
||||
allowFreeEntries: {type: Boolean}
|
||||
allowFreeEntries: {type: Boolean},
|
||||
|
||||
searchOptions: {type: Object}
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,7 +118,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
width: 100%;
|
||||
}
|
||||
::slotted(.search_input.active) {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
::slotted(.no-match) {
|
||||
display: none;
|
||||
@ -120,13 +128,17 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
}
|
||||
|
||||
private _searchTimeout : number;
|
||||
protected static SEARCH_DELAY = 200;
|
||||
protected static SEARCH_TIMEOUT = 500;
|
||||
protected static MIN_CHARS = 2;
|
||||
|
||||
constructor(...args : any[])
|
||||
{
|
||||
super(...args);
|
||||
|
||||
this.search = false;
|
||||
this.searchUrl = "";
|
||||
this.searchOptions = {};
|
||||
|
||||
this._handleSearchButtonClick = this._handleSearchButtonClick.bind(this);
|
||||
this._handleSearchAbort = this._handleSearchAbort.bind(this);
|
||||
this._handleSearchKeyDown = this._handleSearchKeyDown.bind(this);
|
||||
@ -176,6 +188,16 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Do we have the needed properties set, so we can actually do searching
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public get searchEnabled() : boolean
|
||||
{
|
||||
return this.search || this.searchUrl.length > 0;
|
||||
}
|
||||
|
||||
protected get _searchButtonNode()
|
||||
{
|
||||
return this.querySelector("sl-icon[slot='suffix']");
|
||||
@ -232,15 +254,21 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
{
|
||||
super.handleMenuShow();
|
||||
|
||||
this._activeControls.classList.add("active");
|
||||
if(this.searchEnabled)
|
||||
{
|
||||
this._activeControls?.classList.add("active");
|
||||
this._searchInputNode.focus();
|
||||
this._searchInputNode.select();
|
||||
}
|
||||
}
|
||||
|
||||
handleMenuHide()
|
||||
{
|
||||
super.handleMenuHide();
|
||||
this._activeControls.classList.remove("active");
|
||||
if(this.searchEnabled)
|
||||
{
|
||||
this._activeControls?.classList.remove("active");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,7 +278,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
*/
|
||||
protected _handleSearchKeyDown(event : KeyboardEvent)
|
||||
{
|
||||
this._activeControls.classList.add("active");
|
||||
this._activeControls?.classList.add("active");
|
||||
this.dropdown.show();
|
||||
|
||||
// Pass off some keys to select
|
||||
@ -278,7 +306,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
clearTimeout(this._searchTimeout);
|
||||
if(this._searchInputNode.value.length >= Et2WidgetWithSearch.MIN_CHARS)
|
||||
{
|
||||
this._searchTimeout = window.setTimeout(() => {this.startSearch()}, Et2WidgetWithSearch.SEARCH_DELAY);
|
||||
this._searchTimeout = window.setTimeout(() => {this.startSearch()}, Et2WidgetWithSearch.SEARCH_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,8 +326,8 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
|
||||
// Start the searches
|
||||
Promise.all([
|
||||
this.localSearch(this._searchInputNode.value),
|
||||
this.remoteSearch(this._searchInputNode.value)
|
||||
this.localSearch(this._searchInputNode.value, this.searchOptions),
|
||||
this.remoteSearch(this._searchInputNode.value, this.searchOptions)
|
||||
]).then(() =>
|
||||
{
|
||||
spinner.remove();
|
||||
@ -312,7 +340,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
* @param {string} search
|
||||
* @protected
|
||||
*/
|
||||
protected localSearch(search : string) : Promise<void>
|
||||
protected localSearch(search : string, options : object) : Promise<void>
|
||||
{
|
||||
return new Promise((resolve) =>
|
||||
{
|
||||
@ -333,7 +361,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
* @param {string} search
|
||||
* @protected
|
||||
*/
|
||||
protected remoteSearch(search : string)
|
||||
protected remoteSearch(search : string, options : object)
|
||||
{
|
||||
// Remove existing remote items
|
||||
this.remoteItems.forEach(i => i.remove());
|
||||
@ -344,16 +372,15 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
||||
}
|
||||
|
||||
// Fire off the query
|
||||
let promise = this.remoteQuery(search);
|
||||
let promise = this.remoteQuery(search, options);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
protected remoteQuery(search : string)
|
||||
protected remoteQuery(search : string, options : object)
|
||||
{
|
||||
return this.egw().json(this.searchUrl, [search]).sendRequest().then((result) =>
|
||||
{
|
||||
debugger;
|
||||
this.processRemoteResults(result);
|
||||
});
|
||||
}
|
||||
|
@ -44,7 +44,9 @@ import './Et2Favorites/Et2Favorites';
|
||||
import './Et2Image/Et2Image';
|
||||
import './Et2Link/Et2Link';
|
||||
import './Et2Link/Et2LinkAppSelect';
|
||||
import './Et2Link/Et2LinkEntry';
|
||||
import './Et2Link/Et2LinkList';
|
||||
import './Et2Link/Et2LinkSearch';
|
||||
import './Et2Link/Et2LinkString';
|
||||
import './Et2Select/Et2Select';
|
||||
import './Et2Select/Et2SelectAccount';
|
||||
|
Loading…
Reference in New Issue
Block a user