@@ -48,6 +48,7 @@ const icons = {
4848 restore : `<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" fill="currentcolor"><path d="M440-320h80v-166l64 62 56-56-160-160-160 160 56 56 64-62v166ZM280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520Zm-400 0v520-520Z"/></svg>` ,
4949 list : `<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" fill="currentcolor"><path d="M293.08-597.69v-60H820v60H293.08Zm0 147.69v-60H820v60H293.08Zm0 147.69v-60H820v60H293.08ZM172.31-595.38q-13.73 0-23.02-9.4t-9.29-23.3q0-13.56 9.29-22.74 9.29-9.18 23.02-9.18t23.02 9.18q9.29 9.18 9.29 22.74 0 13.9-9.29 23.3t-23.02 9.4Zm0 147.3q-13.73 0-23.02-9.18Q140-466.43 140-480q0-14.31 9.29-23.5t23.02-9.19q13.73 0 23.02 9.19t9.29 23.5q0 13.57-9.29 22.74-9.29 9.18-23.02 9.18Zm0 148.08q-13.73 0-23.02-9.4T140-332.69q0-13.57 9.29-22.75t23.02-9.18q13.73 0 23.02 9.18t9.29 22.75q0 13.89-9.29 23.29-9.29 9.4-23.02 9.4Z"/></svg>` ,
5050 grid : `<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" fill="currentcolor"><path d="M140-520v-300h300v300H140Zm0 380v-300h300v300H140Zm380-380v-300h300v300H520Zm0 380v-300h300v300H520ZM200-580h180v-180H200v180Zm380 0h180v-180H580v180Zm0 380h180v-180H580v180Zm-380 0h180v-180H200v180Zm380-380Zm0 200Zm-200 0Zm0-200Z"/></svg>` ,
51+ gridSmall : `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="3.2" y="3.2" width="4.8" height="4.8" rx="0.8"/><rect x="9.6" y="3.2" width="4.8" height="4.8" rx="0.8"/><rect x="16" y="3.2" width="4.8" height="4.8" rx="0.8"/><rect x="3.2" y="9.6" width="4.8" height="4.8" rx="0.8"/><rect x="9.6" y="9.6" width="4.8" height="4.8" rx="0.8"/><rect x="16" y="9.6" width="4.8" height="4.8" rx="0.8"/><rect x="3.2" y="16" width="4.8" height="4.8" rx="0.8"/><rect x="9.6" y="16" width="4.8" height="4.8" rx="0.8"/><rect x="16" y="16" width="4.8" height="4.8" rx="0.8"/></svg>` ,
5152 sort : `<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" fill="currentcolor"><path d="M140-260v-60h215v60H140Zm0-190v-60h447.31v60H140Zm0-190v-60h680v60H140Z"/></svg>` ,
5253 select : `<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" fill="currentcolor"><path d="m424-325.85 268.92-268.92-42.15-42.15L424-410.15l-114-114L267.85-482 424-325.85ZM212.31-140Q182-140 161-161q-21-21-21-51.31v-535.38Q140-778 161-799q21-21 51.31-21h535.38Q778-820 799-799q21 21 21 51.31v535.38Q820-182 799-161q-21 21-51.31 21H212.31Zm0-60h535.38q4.62 0 8.46-3.85 3.85-3.84 3.85-8.46v-535.38q0-4.62-3.85-8.46-3.84-3.85-8.46-3.85H212.31q-4.62 0-8.46 3.85-3.85 3.84-3.85 8.46v535.38q0 4.62 3.85 8.46 3.84 3.85 8.46 3.85ZM200-760v560-560Z"/></svg>` ,
5354 done : `<svg xmlns="http://www.w3.org/2000/svg" height="18" viewBox="0 -960 960 960" width="18" fill="currentcolor"><path d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"/></svg>` ,
@@ -195,7 +196,7 @@ const TabFiles = {
195196 $row . find ( '.item-modified' ) . text ( window . timeago . format ( file . modified * 1000 ) ) ;
196197 }
197198 if (
198- _this . currentView === 'grid' &&
199+ _this . isGridView ( ) &&
199200 typeof file . thumbnail === 'string' &&
200201 file . thumbnail . length > 0
201202 ) {
@@ -536,17 +537,7 @@ const TabFiles = {
536537 this . initNativeFileDrop ( ) ;
537538
538539 // Apply initial view mode from persisted preferences
539-
540- const $filesContainer = this . $el_window . find ( '.files-tab .files' ) ;
541- const $tabContent = this . $el_window . find ( '.files-tab' ) ;
542- if ( this . currentView === 'grid' ) {
543- $filesContainer . addClass ( 'files-grid-view' ) ;
544- $tabContent . addClass ( 'files-grid-mode' ) ;
545- this . $el_window . find ( '.view-toggle-btn' ) . html ( icons . list ) ;
546- } else {
547- $filesContainer . addClass ( 'files-list-view' ) ;
548- this . $el_window . find ( '.view-toggle-btn' ) . html ( icons . grid ) ;
549- }
540+ this . applyViewMode ( ) ;
550541
551542 // Check for initial file path from URL routing
552543 if ( window . dashboard_initial_file_path ) {
@@ -1270,9 +1261,9 @@ const TabFiles = {
12701261 fileInput . click ( ) ;
12711262 } ;
12721263
1273- // View toggle button
1274- document . querySelector ( '.view-toggle-btn' ) . onclick = ( ) => {
1275- this . toggleView ( ) ;
1264+ // View button (shows dropdown menu: list / compact grid / grid)
1265+ document . querySelector ( '.view-toggle-btn' ) . onclick = ( e ) => {
1266+ this . showViewMenu ( e ) ;
12761267 } ;
12771268
12781269 // Sort button (shows dropdown menu)
@@ -1583,7 +1574,7 @@ const TabFiles = {
15831574 $name . text ( fullName ) ;
15841575 }
15851576 } ) ;
1586- } else if ( this . currentView === 'grid' ) {
1577+ } else if ( this . isGridView ( ) ) {
15871578 // Apply middle-truncation in grid view
15881579 $filesTab . find ( '.files.files-grid-view .row .item-name' ) . each ( function ( ) {
15891580 const $name = $ ( this ) ;
@@ -2516,13 +2507,13 @@ const TabFiles = {
25162507 const $clone = $ ( el_item ) . clone ( ) ;
25172508
25182509 // Wrap in container structure so CSS selectors match
2519- const viewClass = _this . currentView === 'grid' ? 'files-grid-view' : 'files-list-view' ;
2510+ const viewClass = _this . viewClass ( ) ;
25202511 const $wrapper = $ ( `<div class="dashboard-section-files"><div class="files-tab"><div class="files ${ viewClass } "></div></div></div>` ) ;
25212512 $wrapper . find ( '.files' ) . append ( $clone ) ;
25222513
25232514 // In grid view, set fixed width since the grid auto-fill
25242515 // doesn't work without a proper parent width context
2525- if ( _this . currentView === 'grid' ) {
2516+ if ( _this . isGridView ( ) ) {
25262517 $clone . css ( 'width' , $ ( el_item ) . outerWidth ( ) ) ;
25272518 $wrapper . find ( '.files' ) . css ( 'display' , 'block' ) ;
25282519 }
@@ -2556,7 +2547,7 @@ const TabFiles = {
25562547 ui . helper . addClass ( 'selected' ) ;
25572548
25582549 // Clone other selected items with proper container structure
2559- const viewClass = _this . currentView === 'grid' ? 'files-grid-view' : 'files-list-view' ;
2550+ const viewClass = _this . viewClass ( ) ;
25602551 $ ( el_item ) . siblings ( '.row.selected' ) . each ( function ( ) {
25612552 const $clone = $ ( this ) . clone ( ) ;
25622553 const $wrapper = $ ( `<div class="dashboard-section-files item-selected-clone"><div class="files-tab"><div class="files ${ viewClass } "></div></div></div>` ) ;
@@ -2987,37 +2978,102 @@ const TabFiles = {
29872978 } ,
29882979
29892980 /**
2990- * Toggles between list and grid view modes .
2981+ * Whether the current view mode is one of the grid variants .
29912982 *
2992- * Persists the preference to storage.
2983+ * Both 'grid' and 'grid-sm' share the files-grid-view container class
2984+ * (and all grid behavior); 'grid-sm' adds a size-modifier class on top.
2985+ *
2986+ * @returns {boolean }
2987+ */
2988+ isGridView ( ) {
2989+ return this . currentView === 'grid' || this . currentView === 'grid-sm' ;
2990+ } ,
2991+
2992+ /**
2993+ * Container classes for the current view mode, e.g. for drag-ghost wrappers.
2994+ *
2995+ * @returns {string } Space-separated class list for the .files container
2996+ */
2997+ viewClass ( ) {
2998+ if ( this . currentView === 'grid-sm' ) return 'files-grid-view files-grid-view-sm' ;
2999+ if ( this . currentView === 'grid' ) return 'files-grid-view' ;
3000+ return 'files-list-view' ;
3001+ } ,
3002+
3003+ /**
3004+ * Displays the view mode selection menu (list / compact grid / grid).
29933005 *
3006+ * @param {MouseEvent } e - The click event from the view button
29943007 * @returns {void }
29953008 */
2996- toggleView ( ) {
3009+ showViewMenu ( e ) {
3010+ const _this = this ;
3011+
3012+ const viewOptions = [
3013+ { mode : 'list' , label : 'List' } ,
3014+ { mode : 'grid-sm' , label : 'Compact Grid' } ,
3015+ { mode : 'grid' , label : 'Grid' } ,
3016+ ] ;
3017+
3018+ const items = viewOptions . map ( opt => {
3019+ return {
3020+ html : `<span>${ opt . label } </span>` ,
3021+ checked : _this . currentView === opt . mode ,
3022+ onClick : ( ) => {
3023+ _this . setView ( opt . mode ) ;
3024+ } ,
3025+ } ;
3026+ } ) ;
3027+
3028+ UIContextMenu ( {
3029+ items : items ,
3030+ position : { left : e . pageX , top : e . pageY } ,
3031+ } ) ;
3032+ } ,
3033+
3034+ /**
3035+ * Switches to the given view mode and persists the preference.
3036+ *
3037+ * @param {string } mode - 'list', 'grid', or 'grid-sm'
3038+ * @returns {void }
3039+ */
3040+ setView ( mode ) {
3041+ if ( this . currentView === mode ) return ;
3042+ this . currentView = mode ;
3043+ this . applyViewMode ( ) ;
3044+
3045+ puter . kv . set ( 'view_mode' , mode ) ;
3046+
3047+ // Refresh content to update icons for the new view mode
3048+ if ( this . currentPath ) {
3049+ this . renderDirectory ( this . currentPath ) ;
3050+ }
3051+ } ,
3052+
3053+ /**
3054+ * Applies the current view mode's classes and button icon to the DOM.
3055+ *
3056+ * @returns {void }
3057+ */
3058+ applyViewMode ( ) {
29973059 const $filesContainer = this . $el_window . find ( '.files-tab .files' ) ;
29983060 const $toggleBtn = this . $el_window . find ( '.view-toggle-btn' ) ;
29993061 const $tabContent = this . $el_window . find ( '.files-tab' ) ;
30003062
3001- if ( this . currentView === ' list' ) {
3002- this . currentView = 'grid' ;
3003- $filesContainer . removeClass ( 'files-list-view' ) . addClass ( 'files-grid-view' ) ;
3063+ $filesContainer . removeClass ( 'files- list-view files-grid-view files-grid-view-sm' ) ;
3064+ if ( this . isGridView ( ) ) {
3065+ $filesContainer . addClass ( this . viewClass ( ) ) ;
30043066 $tabContent . addClass ( 'files-grid-mode' ) ;
3005- $toggleBtn . html ( icons . list ) ;
3006- $toggleBtn . attr ( 'title' , 'Switch to list view' ) ;
30073067 } else {
3008- this . currentView = 'list' ;
3009- $filesContainer . removeClass ( 'files-grid-view' ) . addClass ( 'files-list-view' ) ;
3068+ $filesContainer . addClass ( 'files-list-view' ) ;
30103069 $tabContent . removeClass ( 'files-grid-mode' ) ;
3011- $toggleBtn . html ( icons . grid ) ;
3012- $toggleBtn . attr ( 'title' , 'Switch to grid view' ) ;
30133070 }
30143071
3015- puter . kv . set ( 'view_mode' , this . currentView ) ;
3016-
3017- // Refresh content to update icons for the new view mode
3018- if ( this . currentPath ) {
3019- this . renderDirectory ( this . currentPath ) ;
3020- }
3072+ // The button shows the active view's icon and opens the view menu
3073+ const btnIcon = this . currentView === 'grid' ? icons . grid
3074+ : this . currentView === 'grid-sm' ? icons . gridSmall
3075+ : icons . list ;
3076+ $toggleBtn . html ( btnIcon ) ;
30213077 } ,
30223078
30233079 /**
0 commit comments