diff --git a/api/js/etemplate/Et2TreeWidget/Et2Tree.ts b/api/js/etemplate/Et2TreeWidget/Et2Tree.ts
new file mode 100644
index 0000000000..890428eb5b
--- /dev/null
+++ b/api/js/etemplate/Et2TreeWidget/Et2Tree.ts
@@ -0,0 +1,256 @@
+import {Et2InputWidget} from "../Et2InputWidget/Et2InputWidget";
+import {SlTree} from "@shoelace-style/shoelace";
+import {Et2Link} from "../Et2Link/Et2Link";
+import {Et2widgetWithSelectMixin} from "../Et2Select/Et2WidgetWithSelectMixin";
+import {et2_no_init} from "../et2_core_common";
+import {egw, framework} from "../../jsapi/egw_global";
+import {SelectOption, find_select_options, cleanSelectOptions} from "../Et2Select/FindSelectOptions";
+import {html, TemplateResult} from "@lion/core";
+import {egwIsMobile} from "../../egw_action/egw_action_common";
+
+export type TreeItem = {
+ child: Boolean | 1,
+ data?: Object,//{sieve:true,...} or {acl:true} or other
+ id: String,
+ im0: String,
+ im1: String,
+ im2: String,
+ item: TreeItem[],
+ checked?: Boolean,
+ nocheckbox: number | Boolean,
+ open: 0 | 1,
+ parent: String,
+ text: String,
+ tooltip: String
+}
+
+
+export class Et2Tree extends Et2widgetWithSelectMixin(SlTree) {
+ private input: any = null;
+ private div: JQuery;
+ private autoloading_url: any;
+ private selectOptions: TreeItem[];
+
+ constructor() {
+ super();
+ }
+
+ static get properties() {
+ return {
+ ...super.properties,
+ multiple: {
+ name: "",
+ type: Boolean,
+ default: false,
+ description: "Allow selecting multiple options"
+ },
+ selectOptions: {
+ type: "any",
+ name: "Select options",
+ default: {},
+ description: "Used to set the tree options."
+ },
+ onClick: {
+ name: "onClick",
+ type: "js",
+ description: "JS code which gets executed when clicks on text of a node"
+ },
+ onSelect: {
+ name: "onSelect",
+ type: "js",
+ default: et2_no_init,
+ description: "Javascript executed when user selects a node"
+ },
+ onCheck: {
+ name: "onCheck",
+ type: "js",
+ default: et2_no_init,
+ description: "Javascript executed when user checks a node"
+ },
+ // TODO do this : --> onChange event is mapped depending on multiple to onCheck or onSelect
+ onOpenStart: {
+ name: "onOpenStart",
+ type: "js",
+ default: et2_no_init,
+ description: "Javascript function executed when user opens a node: function(_id, _widget, _hasChildren) returning true to allow opening!"
+ },
+ onOpenEnd: {
+ name: "onOpenEnd",
+ type: "js",
+ default: et2_no_init,
+ description: "Javascript function executed when opening a node is finished: function(_id, _widget, _hasChildren)"
+ },
+ imagePath: {
+ name: "Image directory",
+ type: String,
+ default: 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"
+ },
+ value: {
+ type: "any",
+ default: {}
+ },
+ actions: {
+ name: "Actions array",
+ type: "any",
+ default: et2_no_init,
+ description: "List of egw actions that can be done on the tree. This includes context menu, drag and drop. TODO: Link to action documentation"
+ },
+ autoLoading: {
+ name: "Auto loading",
+ type: String,
+ default: "",
+ description: "JSON URL or menuaction to be called for nodes marked with child=1, but not having children, GET parameter selected contains node-id"
+ },
+ stdImages: {
+ name: "Standard images",
+ type: String,
+ default: "",
+ description: "comma-separated names of icons for a leaf, closed and opened folder (default: leaf.png,folderClosed.png,folderOpen.png), images with extension get loaded from imagePath, just 'image' or 'appname/image' are allowed too"
+ },
+ multiMarking: {
+ name: "multi marking",
+ type: "any",
+ default: false,
+ description: "Allow marking multiple nodes, default is false which means disabled multiselection, true or 'strict' activates it and 'strict' makes it strict to only same level marking"
+ },
+ highlighting: {
+ name: "highlighting",
+ type: Boolean,
+ default: false,
+ description: "Add highlighting class on hovered over item, highlighting is disabled by default"
+ },
+ }
+ };
+
+ public set onOpenStart(_handler: Function) {
+ this.installHandler("onOpenStart", _handler)
+ }
+
+ public set onChange(_handler: Function) {
+ this.installHandler("onChange", _handler)
+ }
+
+ public set onClick(_handler: Function) {
+ this.installHandler("onClick", _handler)
+ }
+
+ public set onSelect(_handler: Function) {
+ this.installHandler("onSelect", _handler)
+ }
+
+ public set onOpenEnd(_handler: Function) {
+ this.installHandler("onOpenEnd", _handler)
+ }
+
+ _optionTemplate() {
+ // @ts-ignore
+ this.selectOptions= find_select_options(this)[1];
+ //slot = expanded/collapsed instead of expand/collapse like it is in documentation
+ let result: TemplateResult<1> = html``
+ for (const selectOption of this.selectOptions) {
+ result = html`${result}
+