@@ -2,26 +2,31 @@ import { LitElement, html, css } from 'lit';
22
33class QuiDirectoryTree extends LitElement {
44 static properties = {
5- directory : { type : Array } , // Directory data
6- selectedPath : { type : String } // Currently selected path
5+ directory : { type : Array } ,
6+ selectedPath : { type : String } ,
7+ folderSelectable : { type : Boolean } ,
8+ contextMenuItems : { type : Array } // Context menu items for files
79 } ;
810
911 constructor ( ) {
1012 super ( ) ;
1113 this . directory = [ ] ;
1214 this . selectedPath = '' ;
13- this . _collapsedPaths = new Set ( ) ; // Track collapsed nodes
15+ this . folderSelectable = false ;
16+ this . contextMenuItems = [ ] ; // Default: no context menu
17+ this . _collapsedPaths = new Set ( ) ;
1418 }
1519
1620 static styles = css `
1721 :host {
18- --tree-node-font-family: 'Arial', sans-serif;
19- --tree-node-font-size: 14px;
20- --tree-icon-size: 16px;
21- --tree-node-bg-hover: #f0f0f0;
22- --tree-node-bg-selected: #d0e8ff;
23- --tree-node-color: black;
24- --tree-node-selected-color: black;
22+
23+ --tree-node-font-family: var(--lumo-font-family, 'Arial', sans-serif);
24+ --tree-node-font-size: var(--lumo-font-size-m, 14px);
25+ --tree-icon-size: var(--lumo-icon-size-s, 16px);
26+ --tree-node-bg-hover: var(--lumo-contrast-5pct, #f0f0f0);
27+ --tree-node-bg-selected: var(--lumo-primary-color-50pct, #d0e8ff);
28+ --tree-node-color: var(--lumo-body-text-color, black);
29+ --tree-node-selected-color: var(--lumo-body-text-color, black);
2530 --folder-icon-closed: 📁;
2631 --folder-icon-open: 📂;
2732 --file-icon: 📄;
@@ -34,7 +39,6 @@ class QuiDirectoryTree extends LitElement {
3439 font-size: var(--tree-node-font-size);
3540 }
3641 .node {
37- cursor: pointer;
3842 padding: 5px;
3943 border-radius: 5px;
4044 display: flex;
@@ -52,31 +56,64 @@ class QuiDirectoryTree extends LitElement {
5256 .icon {
5357 font-size: var(--tree-icon-size);
5458 }
59+
60+ .label {
61+ cursor: pointer;
62+ }
63+
64+ .label.disabled {
65+ cursor: default;
66+ }
67+
68+ .context-menu {
69+ position: absolute;
70+ background: var(--lumo-base-color, white);
71+ border: 1px solid var(--lumo-base-color, white);
72+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
73+ z-index: 1000;
74+ }
75+ .context-menu-item {
76+ padding: 5px 10px;
77+ cursor: pointer;
78+ }
79+ .context-menu-item:hover {
80+ background-color: var(--tree-node-bg-hover);
81+ }
5582 ` ;
5683
5784 render ( ) {
58- return html `< ul class ="tree "> ${ this . _renderTree ( this . directory , '' ) } </ ul > ` ;
85+ return html `
86+ < ul class ="tree "> ${ this . _renderTree ( this . directory , '' ) } </ ul >
87+ ${ this . _renderContextMenu ( ) }
88+ ` ;
5989 }
6090
6191 _renderTree ( nodes , currentPath ) {
6292 return nodes . map ( ( node ) => {
6393 const path = currentPath ? `${ currentPath } /${ node . name } ` : node . name ;
6494 const isCollapsed = this . _collapsedPaths . has ( path ) ;
95+ const isFolder = node . type === 'folder' ;
96+ const isSelectable = isFolder ? this . folderSelectable : true ;
6597
6698 return html `
6799 < li >
68- < div class =" node ${ this . selectedPath === path ? 'selected' : '' } " >
69- < span
70- class =" icon "
71- @click =" ${ ( e ) => this . _toggleCollapse ( e , path ) } "
72- >
73- ${ node . type === 'folder'
100+ < div
101+ class =" node ${ this . selectedPath === path ? 'selected' : '' } "
102+ @contextmenu =" ${ ( e ) => this . _onContextMenu ( e , path , node ) } "
103+ >
104+ < span class =" icon " @click =" ${ ( e ) => this . _toggleCollapse ( e , path ) } " >
105+ ${ isFolder
74106 ? isCollapsed
75107 ? this . _getIcon ( 'folder-icon-closed' )
76108 : this . _getIcon ( 'folder-icon-open' )
77109 : this . _getIcon ( 'file-icon' ) }
78110 </ span >
79- < span @click ="${ ( e ) => this . _onNodeClick ( e , path , node ) } "> ${ node . name } </ span >
111+ < span
112+ class ="label ${ ! isSelectable ? 'disabled' : '' } "
113+ @click ="${ isSelectable ? ( e ) => this . _onNodeClick ( e , path , node ) : null } "
114+ >
115+ ${ node . name }
116+ </ span >
80117 </ div >
81118 ${ node . children && ! isCollapsed
82119 ? html `< ul class ="tree "> ${ this . _renderTree ( node . children , path ) } </ ul > `
@@ -86,6 +123,26 @@ class QuiDirectoryTree extends LitElement {
86123 } ) ;
87124 }
88125
126+ _renderContextMenu ( ) {
127+ if ( ! this . _contextMenuData ) return '' ;
128+ const { x, y, filePath, node } = this . _contextMenuData ;
129+
130+ return html `
131+ < div class ="context-menu " style ="top: ${ y } px; left: ${ x } px; ">
132+ ${ this . contextMenuItems . map (
133+ ( item ) => html `
134+ < div
135+ class ="context-menu-item "
136+ @click ="${ ( ) => this . _onContextMenuItemClick ( item , filePath , node ) } "
137+ >
138+ ${ item . title }
139+ </ div >
140+ `
141+ ) }
142+ </ div >
143+ ` ;
144+ }
145+
89146 _getIcon ( variableName ) {
90147 return getComputedStyle ( this ) . getPropertyValue ( `--${ variableName } ` ) . trim ( ) || '📄' ;
91148 }
@@ -102,7 +159,10 @@ class QuiDirectoryTree extends LitElement {
102159
103160 _onNodeClick ( event , path , node ) {
104161 event . stopPropagation ( ) ;
162+ if ( node . type === 'folder' && ! this . folderSelectable ) return ;
163+
105164 this . selectedPath = path ;
165+ this . _contextMenuData = null ; // Hide context menu on selection
106166 this . dispatchEvent (
107167 new CustomEvent ( 'file-select' , {
108168 detail : {
@@ -116,6 +176,25 @@ class QuiDirectoryTree extends LitElement {
116176 ) ;
117177 }
118178
179+ _onContextMenu ( event , filePath , node ) {
180+ event . preventDefault ( ) ;
181+ if ( node . type !== 'file' || this . contextMenuItems . length === 0 ) return ;
182+
183+ this . _contextMenuData = {
184+ x : event . clientX ,
185+ y : event . clientY ,
186+ filePath,
187+ node
188+ } ;
189+ this . requestUpdate ( ) ;
190+ }
191+
192+ _onContextMenuItemClick ( item , filePath , node ) {
193+ this . _contextMenuData = null ;
194+ this . requestUpdate ( ) ;
195+ if ( item . callback ) item . callback ( filePath , node ) ;
196+ }
197+
119198 selectFile ( filePath ) {
120199 this . _expandToPath ( filePath ) ;
121200 this . selectedPath = filePath ;
@@ -132,7 +211,7 @@ class QuiDirectoryTree extends LitElement {
132211 }
133212
134213 expandAll ( ) {
135- this . _collapsedPaths . clear ( ) ; // Remove all paths from the collapsed set
214+ this . _collapsedPaths . clear ( ) ;
136215 this . requestUpdate ( ) ;
137216 }
138217
0 commit comments