2024-02-28 22:59:39 +01:00
import { LitElement , nothing , TemplateResult } from "lit" ;
2024-02-15 18:13:01 +01:00
import { html , literal , StaticValue } from "lit/static-html.js" ;
2024-02-23 18:49:16 +01:00
import { Et2Tree , TreeItemData , TreeSearchResult } from "./Et2Tree" ;
2024-02-13 01:19:24 +01:00
import { Et2WidgetWithSelectMixin } from "../Et2Select/Et2WidgetWithSelectMixin" ;
import { property } from "lit/decorators/property.js" ;
import { classMap } from "lit/directives/class-map.js" ;
2024-02-13 17:33:07 +01:00
import { state } from "lit/decorators/state.js" ;
2024-02-13 01:19:24 +01:00
import { HasSlotController } from "../Et2Widget/slot" ;
import { map } from "lit/directives/map.js" ;
2024-02-13 17:33:07 +01:00
import { SlPopup , SlRemoveEvent } from "@shoelace-style/shoelace" ;
2024-02-13 01:19:24 +01:00
import shoelace from "../Styles/shoelace" ;
import styles from "./Et2TreeDropdown.styles" ;
2024-02-13 17:33:07 +01:00
import { Et2Tag } from "../Et2Select/Tag/Et2Tag" ;
2024-02-22 22:33:22 +01:00
import { SearchMixin , SearchResult , SearchResultsInterface } from "../Et2Widget/SearchMixin" ;
import { Et2InputWidgetInterface } from "../Et2InputWidget/Et2InputWidget" ;
2024-02-23 18:49:16 +01:00
interface TreeSearchResults extends SearchResultsInterface < TreeSearchResult >
2024-02-22 22:33:22 +01:00
{
}
type Constructor < T = {} > = new ( . . . args : any [ ] ) = > T ;
2024-02-13 01:19:24 +01:00
/ * *
* @summary A tree that is hidden in a dropdown
*
* @dependency sl - dropdown
* @dependency et2 - tree
* @dependency et2 - tag
*
* @slot label - The input ' s label . Alternatively , you can use the ` label ` attribute .
2024-02-20 17:56:07 +01:00
* @slot prefix - Used to prepend a presentational icon or similar element to the combobox .
* @slot suffix - Used to append a presentational icon or similar element to the input .
* @slot expand - icon - The icon to show when the control is expanded and collapsed . Rotates on open and close .
2024-02-13 01:19:24 +01:00
* @slot help - text - Text that describes how to use the input . Alternatively , you can use the ` help-text ` attribute .
*
* @event change - Emitted when the control ' s value changes .
* @event sl - show - Emitted when the suggestion menu opens .
* @event sl - after - show - Emitted after the suggestion menu opens and all animations are complete .
* @event sl - hide - Emitted when the suggestion menu closes .
* @event sl - after - hide - Emitted after the suggestion menu closes and all animations are complete .
*
* @csspart form - control - The form control that wraps the label , input , and help text .
2024-06-18 11:09:06 +02:00
* @since 23.1 . x
2024-02-13 01:19:24 +01:00
* /
2024-02-23 18:49:16 +01:00
export class Et2TreeDropdown extends SearchMixin < Constructor < any > & Et2InputWidgetInterface & typeof LitElement , TreeSearchResult , TreeSearchResults > ( Et2WidgetWithSelectMixin ( LitElement ) )
2024-02-13 01:19:24 +01:00
{
static get styles ( )
{
return [
shoelace ,
2024-02-22 22:33:22 +01:00
super . styles ,
2024-02-13 01:19:24 +01:00
styles
] ;
}
/** Placeholder text to show as a hint when the select is empty. */
@property ( ) placeholder = "" ;
@property ( { type : Boolean , reflect : true } ) multiple = false ;
/** The component's help text. If you need to display HTML, use the `help-text` slot instead. */
@property ( { attribute : 'help-text' } ) helpText = "" ;
2024-04-22 16:52:47 +02:00
@property ( { type : String } )
autoloading : string = "" //description: "JSON URL or menuaction to be called for nodes marked with child=1, but not having children, getSelectedNode() contains node-id"
2024-02-13 01:19:24 +01:00
/ * *
* Indicates whether the dropdown is open . You can toggle this attribute to show and hide the tree , or you can
* use the ` show() ` and ` hide() ` methods and this attribute will reflect the open state .
* /
@property ( { type : Boolean , reflect : true } ) open = false ;
2024-02-27 19:22:55 +01:00
/ * *
* Actions ( passed to the tree )
* @type { { } }
* /
@property ( { type : Object } ) actions = { } ;
2024-02-13 17:33:07 +01:00
@state ( ) currentTag : Et2Tag ;
2024-02-13 01:19:24 +01:00
2024-02-22 22:33:22 +01:00
// We show search results in the same dropdown
@state ( ) treeOrSearch : "tree" | "search" = "tree" ;
2024-02-13 17:33:07 +01:00
private get _popup ( ) : SlPopup { return this . shadowRoot . querySelector ( "sl-popup" ) }
2024-02-13 01:19:24 +01:00
private get _tree ( ) : Et2Tree { return this . shadowRoot . querySelector ( "et2-tree" ) }
2024-02-13 17:33:07 +01:00
private get _tags ( ) : Et2Tag [ ] { return Array . from ( this . shadowRoot . querySelectorAll ( "et2-tag" ) ) ; }
2024-02-22 22:33:22 +01:00
protected readonly hasSlotController = new HasSlotController ( < LitElement > < unknown > this , "help-text" , "label" ) ;
2024-02-13 01:19:24 +01:00
private __value : string [ ] ;
constructor ( )
{
super ( ) ;
this . __value = [ ] ;
2024-02-27 00:54:07 +01:00
this . handleDocumentClick = this . handleDocumentClick . bind ( this ) ;
}
connectedCallback ( )
{
super . connectedCallback ( ) ;
document . addEventListener ( "click" , this . handleDocumentClick ) ;
}
disconnectedCallback ( )
{
super . disconnectedCallback ( ) ;
document . removeEventListener ( "click" , this . handleDocumentClick ) ;
2024-02-13 01:19:24 +01:00
}
2024-02-23 18:49:16 +01:00
updated ( )
{
// @ts-ignore Popup sometimes loses the anchor which breaks the sizing
this . _popup . handleAnchorChange ( ) ;
}
2024-02-13 01:19:24 +01:00
/** Selected tree leaves */
@property ( )
set value ( new_value : string | string [ ] )
{
if ( typeof new_value === "string" )
{
new_value = new_value . split ( "," )
}
const oldValue = this . __value ;
2024-02-22 22:33:22 +01:00
// Filter to make sure there are no trailing commas or duplicates
this . __value = Array . from ( new Set ( < string [ ] > new_value . filter ( v = > v ) ) ) ;
2024-02-13 01:19:24 +01:00
this . requestUpdate ( "value" , oldValue ) ;
}
get value ( ) : string | string [ ]
{
return this . multiple ? this . __value : (
this . __value ? . length ? this . __value [ 0 ] : ""
) ;
}
2024-02-13 17:33:07 +01:00
/** Sets focus on the control. */
focus ( options? : FocusOptions )
{
this . hasFocus = true ;
// Should not be needed, but not firing the update
this . requestUpdate ( "hasFocus" ) ;
2024-02-22 22:33:22 +01:00
if ( this . _searchNode )
2024-02-13 17:33:07 +01:00
{
2024-02-22 22:33:22 +01:00
this . _searchNode . focus ( options ) ;
2024-02-13 17:33:07 +01:00
}
}
/** Removes focus from the control. */
blur ( )
{
this . open = false ;
2024-02-22 22:33:22 +01:00
this . treeOrSearch = "tree" ;
2024-02-13 17:33:07 +01:00
this . hasFocus = false ;
this . _popup . active = false ;
// Should not be needed, but not firing the update
this . requestUpdate ( "open" ) ;
this . requestUpdate ( "hasFocus" ) ;
2024-02-22 22:33:22 +01:00
this . _searchNode . blur ( ) ;
2024-02-13 17:33:07 +01:00
clearTimeout ( this . _searchTimeout ) ;
}
/** Shows the tree. */
async show ( )
{
if ( this . open || this . disabled )
{
this . open = false ;
this . requestUpdate ( "open" , true ) ;
return undefined ;
}
this . open = true ;
this . requestUpdate ( "open" , false )
return this . updateComplete
}
/** Hides the tree. */
async hide ( )
{
if ( ! this . open || this . disabled )
{
return undefined ;
}
this . open = false ;
this . _popup . active = false ;
2024-02-22 22:33:22 +01:00
this . _searchNode . value = "" ;
2024-02-13 17:33:07 +01:00
this . requestUpdate ( "open" ) ;
return this . updateComplete
}
private setCurrentTag ( tag : Et2Tag )
{
this . _tags . forEach ( t = >
{
t . tabIndex = - 1 ;
if ( t . current )
{
t . current = false ;
t . requestUpdate ( ) ;
}
} ) ;
this . currentTag = tag ;
if ( tag )
{
this . currentTag . tabIndex = 0 ;
this . currentTag . current = true ;
this . currentTag . requestUpdate ( ) ;
this . currentTag . focus ( ) ;
}
}
2024-02-22 22:33:22 +01:00
startSearch ( )
{
super . startSearch ( ) ;
// Show the dropdown, that's where the results will go
2024-07-10 21:13:55 +02:00
this . open = true ;
2024-02-22 22:33:22 +01:00
// Hide the tree
this . treeOrSearch = "search" ;
}
2024-02-23 18:49:16 +01:00
/ * *
* If you have a local list of options , you can search through them on the client and include them in the results .
*
* This is done independently from the server - side search , and the results are merged .
*
* @param { string } search
* @param { object } options
* @returns { Promise < any [ ] > }
* @protected
* /
protected localSearch < DataType extends SearchResult > ( search : string , searchOptions : object , localOptions : DataType [ ] = [ ] ) : Promise < DataType [ ] >
{
return super . localSearch ( search , searchOptions , this . select_options ) ;
}
2024-02-22 22:33:22 +01:00
protected searchResultSelected ( )
{
super . searchResultSelected ( ) ;
if ( this . multiple && typeof this . value !== "undefined" )
{
2024-07-10 21:13:55 +02:00
// Add in the new result(s), no duplicates
this . value = [ . . . new Set ( [ . . . this . value , . . . this . selectedResults . map ( el = > el . value ) ] ) ] ;
2024-02-22 22:33:22 +01:00
}
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" ;
2024-02-23 18:49:16 +01:00
// Close the dropdown
this . hide ( ) ;
this . requestUpdate ( "value" ) ;
2024-02-22 22:33:22 +01:00
}
2024-02-13 17:33:07 +01:00
/ * *
* Keyboard events that the search input did not grab
* ( tags , otion navigation )
*
* @param { KeyboardEvent } event
* /
handleComboboxKeyDown ( event : KeyboardEvent )
{
// Navigate between tags
if ( this . currentTag && ( [ "ArrowLeft" , "ArrowRight" , "Home" , "End" ] . includes ( event . key ) ) )
{
let nextTagIndex = this . _tags . indexOf ( this . currentTag ) ;
const tagCount = this . _tags . length
switch ( event . key )
{
case 'ArrowLeft' :
nextTagIndex -- ;
break ;
case 'ArrowRight' :
nextTagIndex ++ ;
break ;
case 'Home' :
nextTagIndex = 0 ;
break ;
case 'End' :
nextTagIndex = this . _tags . length - 1 ;
break ;
}
nextTagIndex = Math . max ( 0 , nextTagIndex ) ;
if ( nextTagIndex < tagCount && this . _tags [ nextTagIndex ] )
{
this . setCurrentTag ( this . _tags [ nextTagIndex ] ) ;
}
else
{
// Arrow back to search, or got lost
2024-02-22 22:33:22 +01:00
this . _searchNode . focus ( ) ;
2024-02-13 17:33:07 +01:00
}
event . stopPropagation ( ) ;
return false ;
}
// Remove tag
if ( event . target instanceof Et2Tag && [ "Delete" , "Backspace" ] . includes ( event . key ) )
{
const tags = this . _tags ;
let index = tags . indexOf ( event . target ) ;
event . target . dispatchEvent ( new CustomEvent ( 'sl-remove' , { bubbles : true } ) ) ;
index += event . key == "Delete" ? 1 : - 1 ;
if ( index >= 0 && index < tags . length )
{
this . setCurrentTag ( this . _tags [ index ] ) ;
}
else
{
2024-02-22 22:33:22 +01:00
this . _searchNode . focus ( ) ;
2024-02-13 17:33:07 +01:00
}
}
}
2024-02-27 00:54:07 +01:00
protected handleDocumentClick ( event )
{
if ( event . target == this || event . composedPath ( ) . includes ( this ) )
{
return
}
if ( this . open )
{
event . preventDefault ( ) ;
this . hide ( )
}
else
{
this . blur ( ) ;
}
}
2024-02-13 17:33:07 +01:00
private handleSearchFocus ( )
{
this . hasFocus = true ;
// Should not be needed, but not firing the update
this . requestUpdate ( "hasFocus" ) ;
// Reset tags to not take focus
this . setCurrentTag ( null ) ;
}
handleSearchKeyDown ( event )
{
2024-02-22 22:33:22 +01:00
super . handleSearchKeyDown ( event ) ;
2024-02-13 17:33:07 +01:00
// Left at beginning goes to tags
2024-02-22 22:33:22 +01:00
if ( this . _searchNode . selectionStart == 0 && event . key == "ArrowLeft" )
2024-02-13 17:33:07 +01:00
{
this . hide ( ) ;
this . _tags . forEach ( t = > t . tabIndex = 0 ) ;
if ( this . _tags . length > 0 )
{
this . setCurrentTag ( this . _tags [ this . _tags . length - 1 ] ) ;
}
event . stopPropagation ( ) ;
return ;
}
}
protected handleLabelClick ( )
{
2024-02-22 22:33:22 +01:00
this . _searchNode . focus ( ) ;
2024-02-13 17:33:07 +01:00
}
2024-02-13 01:19:24 +01:00
handleTagRemove ( event : SlRemoveEvent , value : string )
{
// Find the tag value and remove it from current value
2024-02-13 17:33:07 +01:00
let valueArray = this . getValueAsArray ( ) ;
2024-02-14 01:15:23 +01:00
const oldValue = valueArray . slice ( 0 ) ;
2024-02-13 17:33:07 +01:00
const index = valueArray . indexOf ( value ) ;
valueArray . splice ( index , 1 ) ;
this . value = valueArray ;
2024-02-14 01:15:23 +01:00
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 } ) ) ;
} ) ;
2024-02-13 01:19:24 +01:00
}
handleTreeChange ( event )
{
2024-02-14 18:42:17 +01:00
const oldValue = this . value . slice ( 0 ) ;
// 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 ( ) ;
2024-02-14 23:26:39 +01:00
if ( id && ! this . value . includes ( id ) )
2024-02-14 18:42:17 +01:00
{
// Copy so LitElement knows it changed
this . value = [ . . . this . value , id ] ;
}
}
2024-02-13 18:40:00 +01:00
2024-02-14 01:15:23 +01:00
this . updateComplete . then ( ( ) = >
{
this . dispatchEvent ( new Event ( "change" , { bubbles : true } ) ) ;
} ) ;
2024-02-13 18:40:00 +01:00
if ( ! this . multiple )
{
this . hide ( ) ;
}
2024-02-13 01:19:24 +01:00
}
handleTriggerClick ( )
{
2024-02-13 17:33:07 +01:00
this . hasFocus = true ;
2024-02-13 01:19:24 +01:00
if ( this . open )
{
this . _popup . active = false ;
2024-02-22 22:33:22 +01:00
this . _searchNode . value = "" ;
2024-02-13 01:19:24 +01:00
}
else
{
this . _popup . active = true ;
}
this . open = this . _popup . active ;
2024-02-22 22:33:22 +01:00
this . treeOrSearch = "tree" ;
2024-02-13 18:40:00 +01:00
this . requestUpdate ( "open" ) ;
this . updateComplete . then ( ( ) = >
{
this . _tree . focus ( ) ;
} )
2024-02-13 01:19:24 +01:00
}
/ * *
* Get the icon for the select option
*
* @param option
* @protected
* /
protected iconTemplate ( option )
{
2024-02-15 18:41:40 +01:00
if ( ! option . icon && ! option . im0 )
2024-02-13 01:19:24 +01:00
{
return html ` ` ;
}
return html `
2024-02-22 22:33:22 +01:00
< et2 - image slot = "prefix" part = "icon" src = "${option.icon ?? option.im0}" > < / e t 2 - i m a g e > `
2024-02-13 01:19:24 +01:00
}
inputTemplate ( )
{
return html `
< input id = "search" type = "text" part = "input"
2024-02-22 22:33:22 +01:00
class = "tree-dropdown__search search__input"
2024-02-13 01:19:24 +01:00
autocomplete = "off"
? disabled = $ { this . disabled }
? readonly = $ { this . readonly }
placeholder = "${this.hasFocus || this.value.length > 0 || this.disabled || this.readonly ? " " : this.placeholder}"
tabindex = "0"
@keydown = $ { this . handleSearchKeyDown }
2024-02-13 17:33:07 +01:00
@blur = $ { ( ) = > { this . hasFocus = false ; } }
2024-02-13 01:19:24 +01:00
@focus = $ { this . handleSearchFocus }
@paste = $ { this . handlePaste }
/ >
` ;
}
2024-02-28 22:59:39 +01:00
styleTemplate ( ) : TemplateResult
{
return html ` ` ;
}
2024-02-15 18:13:01 +01:00
/ * *
* Tag used for rendering tags when multiple = true
* Used for creating , finding & filtering options .
* @see createTagNode ( )
* @returns { string }
* /
public get tagTag ( ) : StaticValue
{
return literal ` et2-tag ` ;
}
2024-02-13 01:19:24 +01:00
tagsTemplate ( )
{
const value = this . getValueAsArray ( ) ;
2024-02-14 18:42:17 +01:00
return html ` ${ map ( value , ( value , index ) = >
2024-02-14 01:15:23 +01:00
{
// Deal with value that is not in options
2024-02-23 18:49:16 +01:00
const option = this . optionSearch ( value , this . select_options , 'value' , 'children' ) ;
2024-02-14 01:15:23 +01:00
return option ? this . tagTemplate ( option ) : nothing ;
2024-02-14 18:42:17 +01:00
} ) } ` ;
2024-02-13 01:19:24 +01:00
}
tagTemplate ( option : TreeItemData )
{
const readonly = ( this . readonly || option && typeof ( option . disabled ) != "undefined" && option . disabled ) ;
const isEditable = false && ! readonly ;
2024-02-14 01:15:23 +01:00
const image = option ? this . iconTemplate ( option ? . option ? ? option ) : null ;
const isValid = true ;
2024-02-15 18:13:01 +01:00
const tagName = this . tagTag ;
2024-02-13 01:19:24 +01:00
return html `
2024-02-15 18:13:01 +01:00
< $ { tagName }
2024-02-13 01:19:24 +01:00
part = "tag"
exportparts = "
base :tag__base ,
content :tag__content ,
remove - button :tag__remove - button ,
remove - button__base :tag__remove - button__base ,
icon :icon
"
class = $ { "tree_tag " + option . class ? ? "" }
tabindex = "-1"
2024-02-14 01:15:23 +01:00
variant = $ { isValid ? nothing : "danger" }
2024-02-13 01:19:24 +01:00
size = $ { this . size || "medium" }
2024-02-22 22:33:22 +01:00
title = $ { option . title }
2024-02-13 01:19:24 +01:00
? removable = $ { ! readonly }
? readonly = $ { readonly }
? editable = $ { isEditable }
. value = $ { option . id }
@sl - remove = $ { ( e : SlRemoveEvent ) = > this . handleTagRemove ( e , option . id ) }
@change = $ { this . handleTagEdit }
@dblclick = $ { this . _handleDoubleClick }
@click = $ { typeof this . onTagClick == "function" ? ( e ) = > this . onTagClick ( e , e . target ) : nothing }
>
$ { image ? ? nothing }
2024-02-14 23:26:39 +01:00
$ { ( option . label ? ? option . text ) . trim ( ) }
2024-02-15 18:13:01 +01:00
< / $ { t a g N a m e } >
2024-02-13 01:19:24 +01:00
` ;
}
public render ( )
{
const hasLabelSlot = this . hasSlotController . test ( 'label' ) ;
const hasHelpTextSlot = this . hasSlotController . test ( 'help-text' ) ;
const hasLabel = this . label ? true : ! ! hasLabelSlot ;
2024-02-27 00:54:07 +01:00
const hasValue = this . value && this . value . length > 0 ;
2024-02-13 01:19:24 +01:00
const hasHelpText = this . helpText ? true : ! ! hasHelpTextSlot ;
const isPlaceholderVisible = this . placeholder && this . value . length === 0 && ! this . disabled && ! this . readonly ;
return html `
< div
part = "form-control"
class = $ { classMap ( {
'form-control' : true ,
'form-control--medium' : true ,
'form-control--has-label' : hasLabel ,
'form-control--has-help-text' : hasHelpText
} ) }
>
< label
id = "label"
part = "form-control-label"
class = "form-control__label"
aria - hidden = $ { hasLabel ? 'false' : 'true' }
>
< slot name = "label" > $ { this . label } < / slot >
< / label >
< div part = "form-control-input" class = "form-control-input" >
< sl - popup
class = $ { classMap ( {
"tree-dropdown" : true ,
input : true ,
'tree-dropdown--open' : this . open ,
'tree-dropdown--disabled' : this . disabled ,
'tree-dropdown--readonly' : this . readonly ,
'tree-dropdown--focused' : this . hasFocus ,
'tree-dropdown--placeholder-visible' : isPlaceholderVisible ,
2024-02-27 00:54:07 +01:00
'tree-dropdown--searching' : this . treeOrSearch == "search" ,
'tree-dropdown--has-value' : hasValue
2024-02-13 01:19:24 +01:00
} ) }
flip
shift
sync = "width"
auto - size = "vertical"
auto - size - padding = "10"
? active = $ { this . open }
placement = $ { this . placement || "bottom" }
stay - open - on - select
? disabled = $ { this . disabled }
>
< div
part = "combobox"
class = "tree-dropdown__combobox"
slot = "anchor"
@keydown = $ { this . handleComboboxKeyDown }
>
< slot part = "prefix" name = "prefix" class = "tree-dropdown__prefix" > < / slot >
2024-02-13 17:33:07 +01:00
< div part = "tags" class = "tree-dropdown__tags" >
$ { this . tagsTemplate ( ) }
2024-02-13 18:40:00 +01:00
$ { this . inputTemplate ( ) }
2024-02-13 17:33:07 +01:00
< / div >
2024-02-13 01:19:24 +01:00
< slot part = "suffix" name = "suffix" class = "tree-dropdown__suffix" > < / slot >
2024-02-13 18:40:00 +01:00
< slot name = "expand-icon" part = "expand-icon" class = "tree-dropdown__expand-icon"
@click = $ { this . handleTriggerClick } >
< sl - icon library = "system" name = "chevron-down" aria - hidden = "true" > < / s l - i c o n >
< / slot >
2024-02-13 01:19:24 +01:00
< / div >
2024-02-22 22:33:22 +01:00
$ { this . searchResultsTemplate ( ) }
2024-02-13 01:19:24 +01:00
< et2 - tree
2024-02-27 19:22:55 +01:00
. id = $ { this . id + "_tree" }
. _parent = $ { this }
2024-02-13 01:19:24 +01:00
class = "tree-dropdown__tree"
2024-02-28 22:59:39 +01:00
exportparts = ""
2024-02-13 01:19:24 +01:00
? readonly = $ { this . readonly }
? disabled = $ { this . disabled }
2024-02-14 18:42:17 +01:00
value = $ { this . multiple ? nothing : this.value }
2024-02-13 01:19:24 +01:00
. _selectOptions = $ { this . select_options }
2024-02-27 01:03:57 +01:00
. actions = $ { this . actions }
2024-02-28 22:59:39 +01:00
. styleTemplate = $ { ( ) = > this . styleTemplate ( ) }
2024-04-22 16:52:47 +02:00
. autoloading = "${this.autoloading}"
2024-02-13 01:19:24 +01:00
2024-02-13 18:40:00 +01:00
@sl - selection - change = $ { this . handleTreeChange }
2024-02-13 01:19:24 +01:00
>
< / e t 2 - t r e e >
< / s l - p o p u p >
< / div >
2024-02-27 19:22:55 +01:00
< div
part = "form-control-help-text"
id = "help-text"
class = "form-control__help-text"
aria - hidden = $ { hasHelpText ? 'false' : 'true' }
>
< slot name = "help-text" > $ { this . helpText } < / slot >
< / div >
2024-02-13 01:19:24 +01:00
`
}
}
2024-02-22 22:33:22 +01:00
// @ts-ignore Type problems because of Et2WidgetWithSelectMixin
2024-02-15 18:13:01 +01:00
customElements . define ( "et2-tree-dropdown" , Et2TreeDropdown ) ;