Skip to content

Commit e726c19

Browse files
committed
Add compact grid option to Files view menu
Reworked the Files view toggle into a context menu with List, Compact Grid (`grid-sm`), and Grid modes, and refactored view handling into shared helpers (`isGridView`, `viewClass`, `setView`, `applyViewMode`) so grid behavior is applied consistently across rendering and drag previews. Added compact-grid CSS overrides to reduce tile, icon, text, and badge sizing while reusing existing grid layout behavior.
1 parent 6643642 commit e726c19

2 files changed

Lines changed: 128 additions & 37 deletions

File tree

src/gui/src/UI/Dashboard/TabFiles.js

Lines changed: 93 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -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
/**

src/gui/src/css/dashboard.css

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,6 +2263,41 @@ body.myapps-reordering .myapps-tile {
22632263
text-align: center;
22642264
}
22652265

2266+
/* --- Compact grid view ---
2267+
The container carries both files-grid-view and files-grid-view-sm, so all
2268+
grid rules above apply; this block only shrinks tile/icon/badge sizing. */
2269+
2270+
.dashboard-section-files .files-tab .files.files-grid-view.files-grid-view-sm {
2271+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
2272+
gap: 8px;
2273+
}
2274+
2275+
.dashboard-section-files .files-tab .files.files-grid-view.files-grid-view-sm .row {
2276+
padding: 8px;
2277+
gap: 6px;
2278+
}
2279+
2280+
.dashboard-section-files .files-tab .files.files-grid-view.files-grid-view-sm .row .item-icon {
2281+
width: 80px;
2282+
height: 80px;
2283+
border-radius: 6px;
2284+
}
2285+
2286+
.dashboard-section-files .files-tab .files.files-grid-view.files-grid-view-sm .row .item-icon svg {
2287+
width: 40px;
2288+
height: 40px;
2289+
}
2290+
2291+
.dashboard-section-files .files-tab .files.files-grid-view.files-grid-view-sm .row .item-name {
2292+
font-size: 12px;
2293+
}
2294+
2295+
.dashboard-section-files .files-tab .files.files-grid-view.files-grid-view-sm .row img.item-badge {
2296+
width: 16px !important;
2297+
height: 16px !important;
2298+
margin: 3px;
2299+
}
2300+
22662301
.dashboard-section-files .files-tab.files-grid-mode .header .columns {
22672302
display: none;
22682303
}

0 commit comments

Comments
 (0)