Et2TreeDropdown: server round-trip working

This commit is contained in:
nathan 2024-02-14 10:42:17 -07:00
parent c6bf9954ae
commit 8b21d21291
3 changed files with 37 additions and 18 deletions

View File

@ -77,7 +77,7 @@ export class Et2Tree extends Et2WidgetWithSelectMixin(LitElement)
imagePath: String = egw().webserverUrl + "/api/templates/default/images/dhtmlxtree/" //TODO we will need a different path here! maybe just rename the path? imagePath: String = egw().webserverUrl + "/api/templates/default/images/dhtmlxtree/" //TODO we will need a different path here! maybe just rename the path?
// description: "Directory for tree structure images, set on server-side to 'dhtmlx' subdir of templates image-directory" // description: "Directory for tree structure images, set on server-side to 'dhtmlx' subdir of templates image-directory"
@property() @property()
value = {} value = []
protected autoloading_url: any; protected autoloading_url: any;
// private selectOptions: TreeItemData[] = []; // private selectOptions: TreeItemData[] = [];

View File

@ -5,7 +5,6 @@ import {property} from "lit/decorators/property.js";
import {classMap} from "lit/directives/class-map.js"; import {classMap} from "lit/directives/class-map.js";
import {state} from "lit/decorators/state.js"; import {state} from "lit/decorators/state.js";
import {HasSlotController} from "../Et2Widget/slot"; import {HasSlotController} from "../Et2Widget/slot";
import {keyed} from "lit/directives/keyed.js";
import {map} from "lit/directives/map.js"; import {map} from "lit/directives/map.js";
import {SlPopup, SlRemoveEvent} from "@shoelace-style/shoelace"; import {SlPopup, SlRemoveEvent} from "@shoelace-style/shoelace";
import shoelace from "../Styles/shoelace"; import shoelace from "../Styles/shoelace";
@ -344,9 +343,22 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
handleTreeChange(event) handleTreeChange(event)
{ {
const oldValue = this.value; const oldValue = this.value.slice(0);
this.value = event?.detail?.selection?.map(i => i.id) ?? [];
this.requestUpdate("value", oldValue); // For single value, we can just grab selected from the tree. For multiple, we need to manage it.
if(!this.multiple)
{
this.value = event?.detail?.selection?.map(i => i.id) ?? []
}
else
{
const id = event?.detail?.selection?.map(i => i.id).pop();
if(id)
{
// Copy so LitElement knows it changed
this.value = [...this.value, id];
}
}
this.updateComplete.then(() => this.updateComplete.then(() =>
{ {
@ -417,12 +429,12 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
tagsTemplate() tagsTemplate()
{ {
const value = this.getValueAsArray(); const value = this.getValueAsArray();
return html`${keyed(this._valueUID, map(value, (value, index) => return html`${map(value, (value, index) =>
{ {
// Deal with value that is not in options // Deal with value that is not in options
const option = this.optionSearch(value, this.select_options); const option = this.optionSearch(value, this.select_options, 'item');
return option ? this.tagTemplate(option) : nothing; return option ? this.tagTemplate(option) : nothing;
}))}`; })}`;
} }
tagTemplate(option : TreeItemData) tagTemplate(option : TreeItemData)
@ -530,10 +542,9 @@ export class Et2TreeDropdown extends Et2WidgetWithSelectMixin(LitElement)
</div> </div>
<et2-tree <et2-tree
class="tree-dropdown__tree" class="tree-dropdown__tree"
multiple=${this.multiple}
?readonly=${this.readonly} ?readonly=${this.readonly}
?disabled=${this.disabled} ?disabled=${this.disabled}
value=${this.value} value=${this.multiple ? nothing : this.value}
._selectOptions=${this.select_options} ._selectOptions=${this.select_options}
@sl-selection-change=${this.handleTreeChange} @sl-selection-change=${this.handleTreeChange}

View File

@ -225,7 +225,9 @@ class Tree extends Etemplate\Widget
{ {
return (boolean)array_filter($cats, function($cat) use($id) return (boolean)array_filter($cats, function($cat) use($id)
{ {
return $cat['id'] == $id; return $cat['id'] == $id || (
!empty($cat['item']) && is_array($cat['item']) && static::in_cats($id, $cat['item'])
);
}); });
} }
@ -246,6 +248,8 @@ class Tree extends Etemplate\Widget
$ok = true; $ok = true;
if (!$this->is_readonly($cname, $form_name)) if (!$this->is_readonly($cname, $form_name))
{ {
$unavailable_name = $form_name . self::UNAVAILABLE_CAT_POSTFIX;
$unavailable_values = (array)self::$request->preserv[$unavailable_name];
$value = $value_in = self::get_array($content, $form_name); $value = $value_in = self::get_array($content, $form_name);
// we can not validate if autoloading is enabled // we can not validate if autoloading is enabled
@ -255,12 +259,16 @@ class Tree extends Etemplate\Widget
$allowed += self::selOptions($form_name); $allowed += self::selOptions($form_name);
foreach((array) $value as $val) foreach((array) $value as $val)
{ {
if(in_array($val, $unavailable_values))
{
continue;
}
if ($this->type == 'tree-cat' && !($this->attrs['multiple'] && !$val) && !self::in_cats($val, $allowed) || if ($this->type == 'tree-cat' && !($this->attrs['multiple'] && !$val) && !self::in_cats($val, $allowed) ||
$this->type == 'tree' && !self::in_tree($val, $allowed)) $this->type == 'tree' && !self::in_tree($val, $allowed))
{ {
self::set_validation_error($form_name,lang("'%1' is NOT allowed%2)!", $val, self::set_validation_error($form_name,lang("'%1' is NOT allowed%2)!", $val,
$this->type == 'tree-cat' ? " ('".implode("','",array_keys($allowed)).')' : ''), ''); $this->type == 'tree-cat' ? " ('".implode("','",array_keys($allowed)).')' : ''), '');
$value = ''; $val = '';
break; break;
} }
} }
@ -269,7 +277,6 @@ class Tree extends Etemplate\Widget
if (is_array($value) && $this->type == 'tree-cat') if (is_array($value) && $this->type == 'tree-cat')
{ {
// unavailable cats need to be merged in again // unavailable cats need to be merged in again
$unavailable_name = $form_name.self::UNAVAILABLE_CAT_POSTFIX;
if (isset(self::$request->preserv[$unavailable_name])) if (isset(self::$request->preserv[$unavailable_name]))
{ {
if ($this->attrs['multiple']) if ($this->attrs['multiple'])
@ -474,16 +481,16 @@ class Tree extends Etemplate\Widget
} }
$cat2path=array(); $cat2path=array();
static::processCategory(0, $options, $categories, !$type); static::processCategory(0, $options, $categories, !$type, $cat2path);
// change cat-ids to pathes and preserv unavailible cats (eg. private user-cats) // change cat-ids to pathes and preserv unavailible cats (eg. private user-cats)
if ($value) if ($value)
{ {
$pathes = $unavailable = array(); $pathes = $unavailable = array();
foreach(is_array($value) ? $value : explode(',',$value) as $cat) foreach(is_array($value) ? $value : explode(',',$value) as $cat)
{ {
if (isset($cat2path[$cat])) if(in_array($cat, $cat2path))
{ {
$pathes[] = $cat2path[$cat]; $pathes[] = $cat;
} }
else else
{ {
@ -508,7 +515,7 @@ class Tree extends Etemplate\Widget
return $options; return $options;
} }
protected static function processCategory($cat_id, &$options, &$categories, $globals) protected static function processCategory($cat_id, &$options, &$categories, $globals, &$cat_id_list)
{ {
foreach((array)$categories->return_array($cat_id ? 'subs' : 'mains', 0, false, '', 'ASC', '', $globals, $cat_id) as $cat) foreach((array)$categories->return_array($cat_id ? 'subs' : 'mains', 0, false, '', 'ASC', '', $globals, $cat_id) as $cat)
{ {
@ -533,11 +540,12 @@ class Tree extends Etemplate\Widget
'label' => $s, 'label' => $s,
'title' => $cat['description'] 'title' => $cat['description']
); );
$cat_id_list[] = $cat['id'];
if(!empty($cat['children'])) if(!empty($cat['children']))
{ {
$category['item'] = []; $category['item'] = [];
unset($cat['children']); unset($cat['children']);
static::processCategory($cat['id'], $category['item'], $categories, $globals); static::processCategory($cat['id'], $category['item'], $categories, $globals, $cat_id_list);
} }
$options[] = $category; $options[] = $category;
} }