mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-03 04:29:28 +01:00
Implement searching in Et2TreeDropdown
This commit is contained in:
parent
3b823bd9ed
commit
b0e8666ecb
@ -146,6 +146,14 @@ export default css`
|
|||||||
order: 20;
|
order: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search__results {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-dropdown--searching .search__results {
|
||||||
|
display: initial;;
|
||||||
|
}
|
||||||
|
|
||||||
/* Tree */
|
/* Tree */
|
||||||
|
|
||||||
sl-popup::part(popup) {
|
sl-popup::part(popup) {
|
||||||
@ -170,4 +178,8 @@ export default css`
|
|||||||
et2-tree::part(checkbox) {
|
et2-tree::part(checkbox) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tree-dropdown--searching et2-tree {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
`;
|
`;
|
@ -11,6 +11,15 @@ import {SlPopup, SlRemoveEvent} from "@shoelace-style/shoelace";
|
|||||||
import shoelace from "../Styles/shoelace";
|
import shoelace from "../Styles/shoelace";
|
||||||
import styles from "./Et2TreeDropdown.styles";
|
import styles from "./Et2TreeDropdown.styles";
|
||||||
import {Et2Tag} from "../Et2Select/Tag/Et2Tag";
|
import {Et2Tag} from "../Et2Select/Tag/Et2Tag";
|
||||||
|
import {SearchMixin, SearchResult, SearchResultsInterface} from "../Et2Widget/SearchMixin";
|
||||||
|
import {Et2InputWidgetInterface} from "../Et2InputWidget/Et2InputWidget";
|
||||||
|
|
||||||
|
|
||||||
|
interface TreeSearchResult extends SearchResultsInterface<SearchResult>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
type Constructor<T = {}> = new (...args : any[]) => T;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @summary A tree that is hidden in a dropdown
|
* @summary A tree that is hidden in a dropdown
|
||||||
@ -34,14 +43,14 @@ import {Et2Tag} from "../Et2Select/Tag/Et2Tag";
|
|||||||
* @csspart form-control - The form control that wraps the label, input, and help text.
|
* @csspart form-control - The form control that wraps the label, input, and help text.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
export class Et2TreeDropdown extends SearchMixin<Constructor<any> & Et2InputWidgetInterface & typeof LitElement, SearchResult, TreeSearchResult>(Et2WidgetWithSelectMixin(LitElement))
|
||||||
{
|
{
|
||||||
|
|
||||||
static get styles()
|
static get styles()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
shoelace,
|
shoelace,
|
||||||
...super.styles,
|
super.styles,
|
||||||
styles
|
styles
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -60,23 +69,19 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
*/
|
*/
|
||||||
@property({type: Boolean, reflect: true}) open = false;
|
@property({type: Boolean, reflect: true}) open = false;
|
||||||
|
|
||||||
@state() searching = false;
|
|
||||||
@state() hasFocus = false;
|
|
||||||
@state() currentTag : Et2Tag;
|
@state() currentTag : Et2Tag;
|
||||||
|
|
||||||
|
// We show search results in the same dropdown
|
||||||
|
@state() treeOrSearch : "tree" | "search" = "tree";
|
||||||
|
|
||||||
private get _popup() : SlPopup { return this.shadowRoot.querySelector("sl-popup")}
|
private get _popup() : SlPopup { return this.shadowRoot.querySelector("sl-popup")}
|
||||||
private get _tree() : Et2Tree { return this.shadowRoot.querySelector("et2-tree")}
|
private get _tree() : Et2Tree { return this.shadowRoot.querySelector("et2-tree")}
|
||||||
|
|
||||||
private get _search() : HTMLInputElement { return this.shadowRoot.querySelector("#search")}
|
|
||||||
|
|
||||||
private get _tags() : Et2Tag[] { return Array.from(this.shadowRoot.querySelectorAll("et2-tag"));}
|
private get _tags() : Et2Tag[] { return Array.from(this.shadowRoot.querySelectorAll("et2-tag"));}
|
||||||
|
|
||||||
protected readonly hasSlotController = new HasSlotController(this, "help-text", "label");
|
protected readonly hasSlotController = new HasSlotController(<LitElement><unknown>this, "help-text", "label");
|
||||||
private __value : string[];
|
private __value : string[];
|
||||||
|
|
||||||
protected _searchTimeout : number;
|
|
||||||
protected _searchPromise : Promise<TreeItemData[]> = Promise.resolve([]);
|
|
||||||
|
|
||||||
constructor()
|
constructor()
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
@ -93,8 +98,8 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
new_value = new_value.split(",")
|
new_value = new_value.split(",")
|
||||||
}
|
}
|
||||||
const oldValue = this.__value;
|
const oldValue = this.__value;
|
||||||
// Filter to make sure there are no trailing commas
|
// Filter to make sure there are no trailing commas or duplicates
|
||||||
this.__value = <string[]>new_value.filter(v => v);
|
this.__value = Array.from(new Set(<string[]>new_value.filter(v => v)));
|
||||||
this.requestUpdate("value", oldValue);
|
this.requestUpdate("value", oldValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,9 +117,9 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
// Should not be needed, but not firing the update
|
// Should not be needed, but not firing the update
|
||||||
this.requestUpdate("hasFocus");
|
this.requestUpdate("hasFocus");
|
||||||
|
|
||||||
if(this._search)
|
if(this._searchNode)
|
||||||
{
|
{
|
||||||
this._search.focus(options);
|
this._searchNode.focus(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,12 +127,13 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
blur()
|
blur()
|
||||||
{
|
{
|
||||||
this.open = false;
|
this.open = false;
|
||||||
|
this.treeOrSearch = "tree";
|
||||||
this.hasFocus = false;
|
this.hasFocus = false;
|
||||||
this._popup.active = false;
|
this._popup.active = false;
|
||||||
// Should not be needed, but not firing the update
|
// Should not be needed, but not firing the update
|
||||||
this.requestUpdate("open");
|
this.requestUpdate("open");
|
||||||
this.requestUpdate("hasFocus");
|
this.requestUpdate("hasFocus");
|
||||||
this._search.blur();
|
this._searchNode.blur();
|
||||||
|
|
||||||
clearTimeout(this._searchTimeout);
|
clearTimeout(this._searchTimeout);
|
||||||
}
|
}
|
||||||
@ -158,6 +164,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
|
|
||||||
this.open = false;
|
this.open = false;
|
||||||
this._popup.active = false;
|
this._popup.active = false;
|
||||||
|
this._searchNode.value = "";
|
||||||
this.requestUpdate("open");
|
this.requestUpdate("open");
|
||||||
return this.updateComplete
|
return this.updateComplete
|
||||||
}
|
}
|
||||||
@ -183,6 +190,37 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
startSearch()
|
||||||
|
{
|
||||||
|
super.startSearch();
|
||||||
|
|
||||||
|
// Show the dropdown, that's where the results will go
|
||||||
|
this.show();
|
||||||
|
|
||||||
|
// Hide the tree
|
||||||
|
this.treeOrSearch = "search";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected searchResultSelected()
|
||||||
|
{
|
||||||
|
super.searchResultSelected();
|
||||||
|
|
||||||
|
if(this.multiple && typeof this.value !== "undefined")
|
||||||
|
{
|
||||||
|
// Add in the new result(s)
|
||||||
|
(<string[]>this.value).splice(this.value.length, 0, ...this.selectedResults.map(el => el.value));
|
||||||
|
}
|
||||||
|
else if(typeof this.value !== "undefined")
|
||||||
|
{
|
||||||
|
// Just replace our value with whatever they chose
|
||||||
|
this.value = this.selectedResults[0]?.value ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done with search, show the tree
|
||||||
|
this.treeOrSearch = "tree";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keyboard events that the search input did not grab
|
* Keyboard events that the search input did not grab
|
||||||
* (tags, otion navigation)
|
* (tags, otion navigation)
|
||||||
@ -219,7 +257,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Arrow back to search, or got lost
|
// Arrow back to search, or got lost
|
||||||
this._search.focus();
|
this._searchNode.focus();
|
||||||
}
|
}
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return false;
|
return false;
|
||||||
@ -237,7 +275,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._search.focus();
|
this._searchNode.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -247,20 +285,20 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
this.hasFocus = true;
|
this.hasFocus = true;
|
||||||
// Should not be needed, but not firing the update
|
// Should not be needed, but not firing the update
|
||||||
this.requestUpdate("hasFocus");
|
this.requestUpdate("hasFocus");
|
||||||
this.show();
|
this.hide();
|
||||||
|
|
||||||
// Reset tags to not take focus
|
// Reset tags to not take focus
|
||||||
this.setCurrentTag(null);
|
this.setCurrentTag(null);
|
||||||
|
|
||||||
this._search.setSelectionRange(this._search.value.length, this._search.value.length);
|
this._searchNode.setSelectionRange(this._searchNode.value.length, this._searchNode.value.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchKeyDown(event)
|
handleSearchKeyDown(event)
|
||||||
{
|
{
|
||||||
clearTimeout(this._searchTimeout);
|
super.handleSearchKeyDown(event);
|
||||||
|
|
||||||
// Left at beginning goes to tags
|
// Left at beginning goes to tags
|
||||||
if(this._search.selectionStart == 0 && event.key == "ArrowLeft")
|
if(this._searchNode.selectionStart == 0 && event.key == "ArrowLeft")
|
||||||
{
|
{
|
||||||
this.hide();
|
this.hide();
|
||||||
this._tags.forEach(t => t.tabIndex = 0);
|
this._tags.forEach(t => t.tabIndex = 0);
|
||||||
@ -271,49 +309,12 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Tab on empty leaves
|
|
||||||
if(this._search.value == "" && event.key == "Tab")
|
|
||||||
{
|
|
||||||
// Propagate, browser will do its thing
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Up / Down navigates options
|
|
||||||
if(['ArrowDown', 'ArrowUp'].includes(event.key) && this._tree)
|
|
||||||
{
|
|
||||||
if(!this.open)
|
|
||||||
{
|
|
||||||
this.show();
|
|
||||||
}
|
|
||||||
event.stopPropagation();
|
|
||||||
this._tree.focus();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start search immediately
|
|
||||||
else if(event.key == "Enter")
|
|
||||||
{
|
|
||||||
event.preventDefault();
|
|
||||||
this.startSearch();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(event.key == "Escape")
|
|
||||||
{
|
|
||||||
this.hide();
|
|
||||||
event.stopPropagation();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the search automatically if they have enough letters
|
|
||||||
// -1 because we're in keyDown handler, and value is from _before_ this key was pressed
|
|
||||||
if(this._search.value.length - 1 > 0)
|
|
||||||
{
|
|
||||||
this._searchTimeout = window.setTimeout(() => {this.startSearch()}, 500);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleLabelClick()
|
protected handleLabelClick()
|
||||||
{
|
{
|
||||||
this._search.focus();
|
this._searchNode.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTagRemove(event : SlRemoveEvent, value : string)
|
handleTagRemove(event : SlRemoveEvent, value : string)
|
||||||
@ -380,12 +381,14 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
if(this.open)
|
if(this.open)
|
||||||
{
|
{
|
||||||
this._popup.active = false;
|
this._popup.active = false;
|
||||||
|
this._searchNode.value = "";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this._popup.active = true;
|
this._popup.active = true;
|
||||||
}
|
}
|
||||||
this.open = this._popup.active;
|
this.open = this._popup.active;
|
||||||
|
this.treeOrSearch = "tree";
|
||||||
this.requestUpdate("open");
|
this.requestUpdate("open");
|
||||||
this.updateComplete.then(() =>
|
this.updateComplete.then(() =>
|
||||||
{
|
{
|
||||||
@ -407,16 +410,14 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<et2-image slot="prefix" part="icon" style="width: var(--icon-width)"
|
<et2-image slot="prefix" part="icon" src="${option.icon ?? option.im0}"></et2-image>`
|
||||||
src="${option.icon ?? option.im0}"></et2-image>`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputTemplate()
|
inputTemplate()
|
||||||
{
|
{
|
||||||
return html`
|
return html`
|
||||||
<input id="search" type="text" part="input"
|
<input id="search" type="text" part="input"
|
||||||
class="tree-dropdown__search"
|
class="tree-dropdown__search search__input"
|
||||||
exportparts="base:search__base"
|
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
?disabled=${this.disabled}
|
?disabled=${this.disabled}
|
||||||
?readonly=${this.readonly}
|
?readonly=${this.readonly}
|
||||||
@ -473,7 +474,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
variant=${isValid ? nothing : "danger"}
|
variant=${isValid ? nothing : "danger"}
|
||||||
size=${this.size || "medium"}
|
size=${this.size || "medium"}
|
||||||
title=${option.path ?? option.title}
|
title=${option.title}
|
||||||
?removable=${!readonly}
|
?removable=${!readonly}
|
||||||
?readonly=${readonly}
|
?readonly=${readonly}
|
||||||
?editable=${isEditable}
|
?editable=${isEditable}
|
||||||
@ -525,6 +526,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
'tree-dropdown--readonly': this.readonly,
|
'tree-dropdown--readonly': this.readonly,
|
||||||
'tree-dropdown--focused': this.hasFocus,
|
'tree-dropdown--focused': this.hasFocus,
|
||||||
'tree-dropdown--placeholder-visible': isPlaceholderVisible,
|
'tree-dropdown--placeholder-visible': isPlaceholderVisible,
|
||||||
|
'tree-dropdown--searching': this.treeOrSearch == "search"
|
||||||
})}
|
})}
|
||||||
flip
|
flip
|
||||||
shift
|
shift
|
||||||
@ -547,15 +549,13 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
${this.tagsTemplate()}
|
${this.tagsTemplate()}
|
||||||
${this.inputTemplate()}
|
${this.inputTemplate()}
|
||||||
</div>
|
</div>
|
||||||
${this.searching ? html`
|
|
||||||
<sl-spinner class="tree-dropdown"></sl-spinner>` : nothing
|
|
||||||
}
|
|
||||||
<slot part="suffix" name="suffix" class="tree-dropdown__suffix"></slot>
|
<slot part="suffix" name="suffix" class="tree-dropdown__suffix"></slot>
|
||||||
<slot name="expand-icon" part="expand-icon" class="tree-dropdown__expand-icon"
|
<slot name="expand-icon" part="expand-icon" class="tree-dropdown__expand-icon"
|
||||||
@click=${this.handleTriggerClick}>
|
@click=${this.handleTriggerClick}>
|
||||||
<sl-icon library="system" name="chevron-down" aria-hidden="true"></sl-icon>
|
<sl-icon library="system" name="chevron-down" aria-hidden="true"></sl-icon>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
|
${this.searchResultsTemplate()}
|
||||||
<et2-tree
|
<et2-tree
|
||||||
class="tree-dropdown__tree"
|
class="tree-dropdown__tree"
|
||||||
?readonly=${this.readonly}
|
?readonly=${this.readonly}
|
||||||
@ -572,4 +572,5 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore Type problems because of Et2WidgetWithSelectMixin
|
||||||
customElements.define("et2-tree-dropdown", Et2TreeDropdown);
|
customElements.define("et2-tree-dropdown", Et2TreeDropdown);
|
@ -4,17 +4,67 @@
|
|||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
import {literal, StaticValue} from "lit/static-html.js";
|
import {literal, StaticValue} from "lit/static-html.js";
|
||||||
|
import {property} from "lit/decorators/property.js";
|
||||||
|
import {PropertyValues} from "lit";
|
||||||
import {Et2TreeDropdown} from "./Et2TreeDropdown";
|
import {Et2TreeDropdown} from "./Et2TreeDropdown";
|
||||||
import {Et2CategoryTag} from "../Et2Select/Tag/Et2CategoryTag";
|
import {Et2CategoryTag} from "../Et2Select/Tag/Et2CategoryTag";
|
||||||
|
|
||||||
export class Et2TreeDropdownCategory extends Et2TreeDropdown
|
export class Et2TreeDropdownCategory extends Et2TreeDropdown
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application to get categories from
|
||||||
|
*/
|
||||||
|
@property({type: String}) application = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include global categories
|
||||||
|
*/
|
||||||
|
@property({type: Boolean}) globalCategories = true;
|
||||||
|
|
||||||
private keep_import : Et2CategoryTag
|
private keep_import : Et2CategoryTag
|
||||||
|
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Taglist::ajax_category_search";
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback()
|
||||||
|
{
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
// Default the application if not set
|
||||||
|
if(!this.application && this.getInstanceManager())
|
||||||
|
{
|
||||||
|
this.application = this.getInstanceManager().app;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the search options from our properties
|
||||||
|
this.searchOptions.application = this.application;
|
||||||
|
this.searchOptions.globalCategories = this.globalCategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
willUpdate(changedProperties : PropertyValues)
|
||||||
|
{
|
||||||
|
super.willUpdate(changedProperties);
|
||||||
|
|
||||||
|
if(changedProperties.has('application'))
|
||||||
|
{
|
||||||
|
this.searchOptions.application = this.application;
|
||||||
|
}
|
||||||
|
if(changedProperties.has('globalCategories'))
|
||||||
|
{
|
||||||
|
this.searchOptions.globalCategories = this.globalCategories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public get tagTag() : StaticValue
|
public get tagTag() : StaticValue
|
||||||
{
|
{
|
||||||
return literal`et2-category-tag`;
|
return literal`et2-category-tag`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore Type problems because of Et2WidgetWithSelectMixin in parent
|
||||||
customElements.define("et2-tree-cat", Et2TreeDropdownCategory);
|
customElements.define("et2-tree-cat", Et2TreeDropdownCategory);
|
@ -128,6 +128,30 @@ class Taglist extends Etemplate\Widget
|
|||||||
return mail_compose::ajax_searchAddress();
|
return mail_compose::ajax_searchAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $search
|
||||||
|
* @param array{application: string, globalCategories: boolean, num_rows: string} $options
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function ajax_category_search($search, $options)
|
||||||
|
{
|
||||||
|
$results = ['results' => []];
|
||||||
|
$start = 0;
|
||||||
|
$categories = new Api\Categories('', $options['application'] ?: '');
|
||||||
|
foreach($categories->return_sorted_array($start, (int)$options['num_rows'], $search, 'ASC', 'cat_name', (boolean)$options['globalCategories'], false) as $cat)
|
||||||
|
{
|
||||||
|
$results['results'][] = Tree::formatCategory($cat, $categories);
|
||||||
|
}
|
||||||
|
$results['total'] = $categories->total_records;
|
||||||
|
|
||||||
|
// switch regular JSON response handling off
|
||||||
|
Api\Json\Request::isJSONRequest(false);
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
echo json_encode($results);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate input
|
* Validate input
|
||||||
*
|
*
|
||||||
|
@ -519,28 +519,7 @@ class Tree extends Etemplate\Widget
|
|||||||
{
|
{
|
||||||
foreach((array)$categories->return_array($cat_id ? 'subs' : 'mains', 0, false, '', 'ASC', 'name', $globals, $cat_id) as $cat)
|
foreach((array)$categories->return_array($cat_id ? 'subs' : 'mains', 0, false, '', 'ASC', 'name', $globals, $cat_id) as $cat)
|
||||||
{
|
{
|
||||||
$s = stripslashes($cat['name']);
|
$category = static::formatCategory($cat, $categories);
|
||||||
|
|
||||||
if($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1')
|
|
||||||
{
|
|
||||||
$s .= ' ♦';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1D array
|
|
||||||
$category = $cat + array(
|
|
||||||
'text' => $s,
|
|
||||||
'path' => $categories->id2name($cat['id'], 'path'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
These ones to play nice when a user puts a tree & a selectbox with the same
|
|
||||||
ID on the form (addressbook edit):
|
|
||||||
if tree overwrites selectbox options, selectbox will still work
|
|
||||||
*/
|
|
||||||
'value' => $cat['id'],
|
|
||||||
'label' => $s,
|
|
||||||
'icon' => $cat['data']['icon'] ?? '',
|
|
||||||
'title' => $cat['description']
|
|
||||||
);
|
|
||||||
$cat_id_list[] = $cat['id'];
|
$cat_id_list[] = $cat['id'];
|
||||||
if(!empty($cat['children']))
|
if(!empty($cat['children']))
|
||||||
{
|
{
|
||||||
@ -552,8 +531,32 @@ class Tree extends Etemplate\Widget
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function ajaxSearch($search, $options)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
public static function formatCategory($cat, &$categories_object)
|
||||||
|
{
|
||||||
|
$s = stripslashes($cat['name']);
|
||||||
|
|
||||||
|
if($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1')
|
||||||
|
{
|
||||||
|
$s .= ' ♦';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1D array
|
||||||
|
$category = $cat + array(
|
||||||
|
// Legacy
|
||||||
|
'text' => $s,
|
||||||
|
'path' => $categories_object->id2name($cat['id'], 'path'),
|
||||||
|
|
||||||
|
//Client side search interface
|
||||||
|
'value' => $cat['id'],
|
||||||
|
'label' => $s,
|
||||||
|
'icon' => $cat['data']['icon'] ?? '',
|
||||||
|
'title' => $cat['description']
|
||||||
|
);
|
||||||
|
if(!empty($cat['children']))
|
||||||
|
{
|
||||||
|
$category['hasChildren'] = true;
|
||||||
|
}
|
||||||
|
return $category;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user