mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-17 03:19:23 +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 {
|
:host {
|
||||||
--icon-width: 20px;
|
--icon-width: 20px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
min-width: 64px;
|
||||||
}
|
}
|
||||||
:host([app_icons]) {
|
:host([app_icons]) {
|
||||||
max-width: 75px;
|
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)
|
private _handleChange(e)
|
||||||
{
|
{
|
||||||
// Set icon
|
// 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;
|
allowFreeEntries : boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional search options passed to the search functions
|
||||||
|
*
|
||||||
|
* @type {object}
|
||||||
|
*/
|
||||||
|
searchOptions : object;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the search process
|
* Start the search process
|
||||||
@ -40,18 +46,18 @@ export declare class SearchMixinInterface
|
|||||||
/**
|
/**
|
||||||
* Search local options
|
* Search local options
|
||||||
*/
|
*/
|
||||||
localSearch(search : string) : Promise<void>
|
localSearch(search : string, options : object) : Promise<void>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search remote options.
|
* Search remote options.
|
||||||
* If searchUrl is not set, it will return very quickly with no results
|
* 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
|
* 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},
|
searchUrl: {type: String},
|
||||||
|
|
||||||
allowFreeEntries: {type: Boolean}
|
allowFreeEntries: {type: Boolean},
|
||||||
|
|
||||||
|
searchOptions: {type: Object}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +118,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
::slotted(.search_input.active) {
|
::slotted(.search_input.active) {
|
||||||
display: block;
|
display: flex;
|
||||||
}
|
}
|
||||||
::slotted(.no-match) {
|
::slotted(.no-match) {
|
||||||
display: none;
|
display: none;
|
||||||
@ -120,13 +128,17 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _searchTimeout : number;
|
private _searchTimeout : number;
|
||||||
protected static SEARCH_DELAY = 200;
|
protected static SEARCH_TIMEOUT = 500;
|
||||||
protected static MIN_CHARS = 2;
|
protected static MIN_CHARS = 2;
|
||||||
|
|
||||||
constructor(...args : any[])
|
constructor(...args : any[])
|
||||||
{
|
{
|
||||||
super(...args);
|
super(...args);
|
||||||
|
|
||||||
|
this.search = false;
|
||||||
|
this.searchUrl = "";
|
||||||
|
this.searchOptions = {};
|
||||||
|
|
||||||
this._handleSearchButtonClick = this._handleSearchButtonClick.bind(this);
|
this._handleSearchButtonClick = this._handleSearchButtonClick.bind(this);
|
||||||
this._handleSearchAbort = this._handleSearchAbort.bind(this);
|
this._handleSearchAbort = this._handleSearchAbort.bind(this);
|
||||||
this._handleSearchKeyDown = this._handleSearchKeyDown.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()
|
protected get _searchButtonNode()
|
||||||
{
|
{
|
||||||
return this.querySelector("sl-icon[slot='suffix']");
|
return this.querySelector("sl-icon[slot='suffix']");
|
||||||
@ -232,15 +254,21 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
{
|
{
|
||||||
super.handleMenuShow();
|
super.handleMenuShow();
|
||||||
|
|
||||||
this._activeControls.classList.add("active");
|
if(this.searchEnabled)
|
||||||
|
{
|
||||||
|
this._activeControls?.classList.add("active");
|
||||||
this._searchInputNode.focus();
|
this._searchInputNode.focus();
|
||||||
this._searchInputNode.select();
|
this._searchInputNode.select();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleMenuHide()
|
handleMenuHide()
|
||||||
{
|
{
|
||||||
super.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)
|
protected _handleSearchKeyDown(event : KeyboardEvent)
|
||||||
{
|
{
|
||||||
this._activeControls.classList.add("active");
|
this._activeControls?.classList.add("active");
|
||||||
this.dropdown.show();
|
this.dropdown.show();
|
||||||
|
|
||||||
// Pass off some keys to select
|
// Pass off some keys to select
|
||||||
@ -278,7 +306,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
clearTimeout(this._searchTimeout);
|
clearTimeout(this._searchTimeout);
|
||||||
if(this._searchInputNode.value.length >= Et2WidgetWithSearch.MIN_CHARS)
|
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
|
// Start the searches
|
||||||
Promise.all([
|
Promise.all([
|
||||||
this.localSearch(this._searchInputNode.value),
|
this.localSearch(this._searchInputNode.value, this.searchOptions),
|
||||||
this.remoteSearch(this._searchInputNode.value)
|
this.remoteSearch(this._searchInputNode.value, this.searchOptions)
|
||||||
]).then(() =>
|
]).then(() =>
|
||||||
{
|
{
|
||||||
spinner.remove();
|
spinner.remove();
|
||||||
@ -312,7 +340,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
* @param {string} search
|
* @param {string} search
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected localSearch(search : string) : Promise<void>
|
protected localSearch(search : string, options : object) : Promise<void>
|
||||||
{
|
{
|
||||||
return new Promise((resolve) =>
|
return new Promise((resolve) =>
|
||||||
{
|
{
|
||||||
@ -333,7 +361,7 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
* @param {string} search
|
* @param {string} search
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
protected remoteSearch(search : string)
|
protected remoteSearch(search : string, options : object)
|
||||||
{
|
{
|
||||||
// Remove existing remote items
|
// Remove existing remote items
|
||||||
this.remoteItems.forEach(i => i.remove());
|
this.remoteItems.forEach(i => i.remove());
|
||||||
@ -344,16 +372,15 @@ export const Et2WithSearchMixin = dedupeMixin((superclass) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fire off the query
|
// Fire off the query
|
||||||
let promise = this.remoteQuery(search);
|
let promise = this.remoteQuery(search, options);
|
||||||
|
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected remoteQuery(search : string)
|
protected remoteQuery(search : string, options : object)
|
||||||
{
|
{
|
||||||
return this.egw().json(this.searchUrl, [search]).sendRequest().then((result) =>
|
return this.egw().json(this.searchUrl, [search]).sendRequest().then((result) =>
|
||||||
{
|
{
|
||||||
debugger;
|
|
||||||
this.processRemoteResults(result);
|
this.processRemoteResults(result);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,9 @@ import './Et2Favorites/Et2Favorites';
|
|||||||
import './Et2Image/Et2Image';
|
import './Et2Image/Et2Image';
|
||||||
import './Et2Link/Et2Link';
|
import './Et2Link/Et2Link';
|
||||||
import './Et2Link/Et2LinkAppSelect';
|
import './Et2Link/Et2LinkAppSelect';
|
||||||
|
import './Et2Link/Et2LinkEntry';
|
||||||
import './Et2Link/Et2LinkList';
|
import './Et2Link/Et2LinkList';
|
||||||
|
import './Et2Link/Et2LinkSearch';
|
||||||
import './Et2Link/Et2LinkString';
|
import './Et2Link/Et2LinkString';
|
||||||
import './Et2Select/Et2Select';
|
import './Et2Select/Et2Select';
|
||||||
import './Et2Select/Et2SelectAccount';
|
import './Et2Select/Et2SelectAccount';
|
||||||
|
Loading…
Reference in New Issue
Block a user