Just get it working WIP

- Fix category tree structure
- Switch on tree multiple
probably lots of bugs still, looks like we may have to do click on tree = add / remove and not show the value after all
This commit is contained in:
nathan 2024-02-13 17:15:23 -07:00
parent 6fa102dfc5
commit 62d9c222b6
6 changed files with 116 additions and 63 deletions

View File

@ -148,7 +148,7 @@
</columns>
<rows>
<row valign="top">
<tree-cat id="cat_id_tree" options="13,,width:99%"/>
<et2-tree-cat id="cat_id_tree" multiple="true" placeholder="Category"/>
<et2-select-cat id="cat_id" width="100%" height="195" multiple="true" placeholder="Category"></et2-select-cat>
<et2-description></et2-description>
<grid width="100%">

View File

@ -58,7 +58,6 @@ export class Et2MultiselectTree extends Et2Tree {
}
_optionTemplate(selectOption: TreeItemData): TemplateResult<1> {
this._currentOption = selectOption
let img: String = selectOption.im0 ?? selectOption.im1 ?? selectOption.im2;
if (img) {
//sl-icon images need to be svgs if there is a png try to find the corresponding svg
@ -70,7 +69,8 @@ export class Et2MultiselectTree extends Et2Tree {
<sl-tree-item
id=${selectOption.id}
?lazy=${this._currentOption.item?.length === 0 && this._currentOption.child}
?selected=${this.value.includes(selectOption.id)}
?lazy=${selectOption.item?.length === 0 && selectOption.child}
@sl-lazy-load=${(event) => {
this.handleLazyLoading(selectOption).then((result) => {
@ -82,8 +82,8 @@ export class Et2MultiselectTree extends Et2Tree {
>
<sl-icon src="${img ?? nothing}"></sl-icon>
${this._currentOption.text}
${repeat(this._currentOption.item, this._optionTemplate.bind(this))}
${selectOption.text}
${(selectOption.item) ? html`${repeat(selectOption.item, this._optionTemplate.bind(this))}` : nothing}
</sl-tree-item>`
}
@ -100,8 +100,10 @@ export class Et2MultiselectTree extends Et2Tree {
}
this.selectedNodes = event.detail.selection;
//TODO look at what signature is expected here
this.onchange(event,this)
if(typeof this.onclick == "function")
{
this.onclick(event.detail.selection[0].id, this, event.detail.previous)
}
}
}

View File

@ -16,12 +16,14 @@ import {EgwDragDropShoelaceTree} from "../../egw_action/EgwDragDropShoelaceTree"
export type TreeItemData = {
focused?: boolean;
// Has children, but they may not be provided in item
child: Boolean | 1,
data?: Object,//{sieve:true,...} or {acl:true} or other
id: string,
im0: String,
im1: String,
im2: String,
// Child items
item: TreeItemData[],
checked?: Boolean,
nocheckbox: number | Boolean,
@ -98,6 +100,8 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
{
super();
this._selectOptions = [];
this._optionTemplate = this._optionTemplate.bind(this);
}
//Sl-Trees handle their own onClick events
@ -592,7 +596,11 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
return html`
<sl-tree-item
part="item"
exportparts="checkbox"
id=${selectOption.id}
title=${selectOption.tooltip || nothing}
?selected=${this.value.includes(selectOption.id)}
?expanded=${(this.calculateExpandState(selectOption))}
?lazy=${selectOption.item?.length === 0 && selectOption.child}
?focused=${selectOption.focused || nothing}
@ -607,7 +615,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
<sl-icon src="${img ?? nothing}"></sl-icon>
${selectOption.text}
${selectOption.item ? repeat(selectOption.item, this._optionTemplate.bind(this)) : ""}
${selectOption.item ? repeat(selectOption.item, this._optionTemplate) : nothing}
</sl-tree-item>`
}
@ -616,6 +624,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
{
return html`
<sl-tree
.selection=${this.multiple ? "multiple" : "single"}
@sl-selection-change=${
(event: any) => {
this._previousOption = this._currentOption
@ -646,7 +655,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
}
>
${repeat(this._selectOptions, this._optionTemplate.bind(this))}
${repeat(this._selectOptions, this._optionTemplate)}
</sl-tree>
`;
}
@ -750,7 +759,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
protected updated(_changedProperties: PropertyValues)
{
this._link_actions(this.actions)
// this._link_actions(this.actions)
super.updated(_changedProperties);
}
@ -772,15 +781,12 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
}
private calculateExpandState = (selectOption: TreeItemData) => {
if (selectOption.id.endsWith("INBOX") || selectOption.id == window.egw.preference("ActiveProfileID", "mail"))
{
return true
}
if (selectOption.open)
{
return true
}
if (
if(this._selectOptions.length > 1 &&
this._selectOptions[0] == selectOption &&
(this._selectOptions.find((selectOption) => {
return selectOption.open
@ -790,7 +796,10 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
{
return true //open the first item, if no item is opened
}
if(selectOption.id && (selectOption.id.endsWith("INBOX") || selectOption.id == window.egw.preference("ActiveProfileID", "mail")))
{
return true
}
return false
;
}
@ -855,8 +864,5 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
}
customElements.define("et2-tree", Et2Tree);
customElements.define("et2-tree-cat", class extends Et2Tree
{
});

View File

@ -44,15 +44,12 @@ export default css`
flex-wrap: nowrap;
align-items: flex-start;
justify-content: space-between;
gap: 0.1rem 0.5rem;
background-color: var(--sl-input-background-color);
border: solid var(--sl-input-border-width) var(--sl-input-border-color);
border-radius: var(--sl-input-border-radius-medium);
font-size: var(--sl-input-font-size-medium);
min-height: var(--sl-input-height-medium);
max-height: calc(var(--height, 5) * var(--sl-input-height-medium));
overflow-y: auto;
overflow-x: hidden;
padding-block: 0;
@ -107,6 +104,9 @@ export default css`
.tree-dropdown__tags {
display: flex;
flex-wrap: wrap;
gap: 0.1rem 0.5rem;
min-height: var(--sl-input-height-medium);
max-height: calc(var(--height, 5) * var(--sl-input-height-medium));
}
/* End tags */
@ -157,14 +157,17 @@ export default css`
border-radius: var(--sl-border-radius-medium);
padding-block: var(--sl-spacing-x-small);
padding-inline: 0;
overflow: auto;
overflow-y: auto;
overflow-x: hidden;
overscroll-behavior: none;
z-index: var(--sl-z-index-dropdown);
/* Make sure it adheres to the popup's auto size */
height: auto;
max-width: var(--auto-size-available-width);
}
/* This doesn't work for some reason, it's overwritten somewhere */
--size: 1.8em;
et2-tree::part(checkbox) {
display: none;
}
`;

View File

@ -90,7 +90,8 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
new_value = new_value.split(",")
}
const oldValue = this.__value;
this.__value = <string[]>new_value;
// Filter to make sure there are no trailing commas
this.__value = <string[]>new_value.filter(v => v);
this.requestUpdate("value", oldValue);
}
@ -316,19 +317,41 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
{
// Find the tag value and remove it from current value
let valueArray = this.getValueAsArray();
const oldValue = valueArray.slice(0);
const index = valueArray.indexOf(value);
valueArray.splice(index, 1);
this.value = valueArray;
this.requestUpdate("value");
this.dispatchEvent(new Event("change", {bubbles: true}));
this.requestUpdate("value", oldValue);
// TODO: Clean up this scope violation
// sl-tree-item is not getting its selected attribute updated
Array.from(this._tree._tree.querySelectorAll('sl-tree-item')).forEach(e =>
{
if(this.value.includes(e.id))
{
e.setAttribute("selected", "");
}
else
{
e.removeAttribute("selected");
}
});
this._tree.requestUpdate();
this.updateComplete.then(() =>
{
this.dispatchEvent(new Event("change", {bubbles: true}));
});
}
handleTreeChange(event)
{
const oldValue = this.value;
this.value = this._tree.getValue();
this.value = event?.detail?.selection?.map(i => i.id) ?? [];
this.requestUpdate("value", oldValue);
this.updateComplete.then(() =>
{
this.dispatchEvent(new Event("change", {bubbles: true}));
});
if(!this.multiple)
{
this.hide();
@ -394,14 +417,21 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
tagsTemplate()
{
const value = this.getValueAsArray();
return html`${keyed(this._valueUID, map(value, (value, index) => this.tagTemplate(this.optionSearch(value, this.select_options))))}`;
return html`${keyed(this._valueUID, map(value, (value, index) =>
{
// Deal with value that is not in options
const option = this.optionSearch(value, this.select_options);
return option ? this.tagTemplate(option) : nothing;
}))}`;
}
tagTemplate(option : TreeItemData)
{
const readonly = (this.readonly || option && typeof (option.disabled) != "undefined" && option.disabled);
const isEditable = false && !readonly;
const image = this.iconTemplate(option?.option ?? option);
const image = option ? this.iconTemplate(option?.option ?? option) : null;
const isValid = true;
return html`
<et2-tag
part="tag"
@ -414,7 +444,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
"
class=${"tree_tag " + option.class ?? ""}
tabindex="-1"
?pill=${this.pill}
variant=${isValid ? nothing : "danger"}
size=${this.size || "medium"}
?removable=${!readonly}
?readonly=${readonly}
@ -468,7 +498,6 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
'tree-dropdown--focused': this.hasFocus,
'tree-dropdown--placeholder-visible': isPlaceholderVisible,
})}
strategy="fixed"
flip
shift
sync="width"
@ -504,7 +533,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
multiple=${this.multiple}
?readonly=${this.readonly}
?disabled=${this.disabled}
.value=${this.value}
value=${this.value}
._selectOptions=${this.select_options}
@sl-selection-change=${this.handleTreeChange}
@ -517,3 +546,7 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
}
customElements.define("et2-tree-dropdown", Et2TreeDropdown);
customElements.define("et2-tree-cat", class extends Et2TreeDropdown
{
});

View File

@ -473,34 +473,8 @@ class Tree extends Etemplate\Widget
$categories = new Api\Categories('',$type3);
}
$cat2path=array();
foreach((array)$categories->return_sorted_array(0,False,'','','',!$type,0,true) as $cat)
{
$s = stripslashes($cat['name']);
if ($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1')
{
$s .= ' &#9830;';
}
$cat2path[$cat['id']] = $path = ($cat['parent'] ? $cat2path[$cat['parent']].'/' : '').(string)$cat['id'];
// 1D array
$options[] = $cat + array(
'text' => $s,
'path' => $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,
'title' => $cat['description']
);
// Tree in array
//$options[$cat['parent']][] = $cat;
}
static::processCategory(0, $options, $categories, !$type);
// change cat-ids to pathes and preserv unavailible cats (eg. private user-cats)
if ($value)
{
@ -533,4 +507,39 @@ class Tree extends Etemplate\Widget
//error_log(__METHOD__."('$widget_type', '$legacy_options', no_lang=".array2string($no_lang).', readonly='.array2string($readonly).", value=$value) returning ".array2string($options));
return $options;
}
protected static function processCategory($cat_id, &$options, &$categories, $globals)
{
foreach((array)$categories->return_array($cat_id ? 'subs' : 'mains', 0, false, '', 'ASC', '', $globals, $cat_id) as $cat)
{
$s = stripslashes($cat['name']);
if($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1')
{
$s .= ' &#9830;';
}
// 1D array
$category = $cat + array(
'text' => $s,
/*
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,
'title' => $cat['description']
);
if(!empty($cat['children']))
{
$category['item'] = [];
unset($cat['children']);
static::processCategory($cat['id'], $category['item'], $categories, $globals);
}
$options[] = $category;
}
}
}