Skip to content

Commit ff30de1

Browse files
committed
feat: customize the order of Explorer sidebar items
1 parent 121043d commit ff30de1

File tree

4 files changed

+182
-7
lines changed

4 files changed

+182
-7
lines changed

src/gui/src/UI/UIDesktop.js

+5
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,11 @@ async function UIDesktop(options){
563563
}
564564
})
565565

566+
// load window sidebar items from KV
567+
puter.kv.get("sidebar_items").then(async (val) => {
568+
window.sidebar_items = val;
569+
})
570+
566571
// Get menubar style
567572
puter.kv.get('menubar_style').then(async (val) => {
568573
let value = val;

src/gui/src/UI/UIWindow.js

+140-6
Original file line numberDiff line numberDiff line change
@@ -265,12 +265,35 @@ async function UIWindow(options) {
265265
>`;
266266
// favorites
267267
h += `<h2 class="window-sidebar-title disable-user-select">${i18n('favorites')}</h2>`;
268-
h += `<div draggable="false" title="${i18n('home')}" class="window-sidebar-item disable-user-select ${options.path === window.home_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.home_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-home.svg'])}">${i18n('home')}</div>`;
269-
h += `<div draggable="false" title="${i18n('documents')}" class="window-sidebar-item disable-user-select ${options.path === window.docs_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.docs_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-documents.svg'])}">${i18n('documents')}</div>`;
270-
h += `<div draggable="false" title="${i18n('public')}" class="window-sidebar-item disable-user-select ${options.path === window.public_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.public_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-public.svg'])}">${i18n('public')}</div>`;
271-
h += `<div draggable="false" title="${i18n('pictures')}" class="window-sidebar-item disable-user-select ${options.path === window.pictures_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.pictures_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-pictures.svg'])}">${i18n('pictures')}</div>`;
272-
h += `<div draggable="false" title="${i18n('desktop')}" class="window-sidebar-item disable-user-select ${options.path === window.desktop_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.desktop_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-desktop.svg'])}">${i18n('desktop')}</div>`;
273-
h += `<div draggable="false" title="${i18n('videos')}" class="window-sidebar-item disable-user-select ${options.path === window.videos_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.videos_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-videos.svg'])}">${i18n('videos')}</div>`;
268+
// default items if sidebar_items is not set
269+
if(!window.sidebar_items){
270+
h += `<div draggable="false" title="${i18n('home')}" class="window-sidebar-item disable-user-select ${options.path === window.home_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.home_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-home.svg'])}">${i18n('home')}</div>`;
271+
h += `<div draggable="false" title="${i18n('documents')}" class="window-sidebar-item disable-user-select ${options.path === window.docs_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.docs_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-documents.svg'])}">${i18n('documents')}</div>`;
272+
h += `<div draggable="false" title="${i18n('public')}" class="window-sidebar-item disable-user-select ${options.path === window.public_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.public_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-public.svg'])}">${i18n('public')}</div>`;
273+
h += `<div draggable="false" title="${i18n('pictures')}" class="window-sidebar-item disable-user-select ${options.path === window.pictures_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.pictures_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-pictures.svg'])}">${i18n('pictures')}</div>`;
274+
h += `<div draggable="false" title="${i18n('desktop')}" class="window-sidebar-item disable-user-select ${options.path === window.desktop_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.desktop_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-desktop.svg'])}">${i18n('desktop')}</div>`;
275+
h += `<div draggable="false" title="${i18n('videos')}" class="window-sidebar-item disable-user-select ${options.path === window.videos_path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(window.videos_path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(window.icons['sidebar-folder-videos.svg'])}">${i18n('videos')}</div>`;
276+
}else{
277+
let items = JSON.parse(window.sidebar_items);
278+
for(let item of items){
279+
let icon;
280+
if(item.path === window.home_path)
281+
icon = window.icons['sidebar-folder-home.svg'];
282+
else if(item.path === window.docs_path)
283+
icon = window.icons['sidebar-folder-documents.svg'];
284+
else if(item.path === window.public_path)
285+
icon = window.icons['sidebar-folder-public.svg'];
286+
else if(item.path === window.pictures_path)
287+
icon = window.icons['sidebar-folder-pictures.svg'];
288+
else if(item.path === window.desktop_path)
289+
icon = window.icons['sidebar-folder-desktop.svg'];
290+
else if(item.path === window.videos_path)
291+
icon = window.icons['sidebar-folder-videos.svg'];
292+
else
293+
icon = window.icons['sidebar-folder.svg'];
294+
h += `<div title="${html_encode(item.label)}" class="window-sidebar-item disable-user-select ${options.path === item.path ? 'window-sidebar-item-active' : ''}" data-path="${html_encode(item.path)}"><img draggable="false" class="window-sidebar-item-icon" src="${html_encode(icon)}">${html_encode(item.name)}</div>`;
295+
}
296+
}
274297
h += `</div>`;
275298
}
276299

@@ -434,6 +457,7 @@ async function UIWindow(options) {
434457
// Append
435458
$(el_body).append(h);
436459

460+
437461
// disable_parent_window
438462
if(options.disable_parent_window && options.parent_uuid !== null){
439463
const $el_parent_window = $(`.window[data-element_uuid="${options.parent_uuid}"]`);
@@ -2369,6 +2393,70 @@ async function UIWindow(options) {
23692393
});
23702394
})
23712395

2396+
//--------------------------------------------------
2397+
// Sidebar sortable
2398+
//--------------------------------------------------
2399+
if(options.is_dir && !isMobile.phone){
2400+
loadSavedSidebarOrder(el_window);
2401+
const $sidebar = $(el_window).find('.window-sidebar');
2402+
2403+
$sidebar.sortable({
2404+
items: '.window-sidebar-item:not(.window-sidebar-title)', // More specific selector
2405+
connectWith: '.window-sidebar',
2406+
cursor: 'move',
2407+
axis: 'y',
2408+
distance: 5,
2409+
containment: 'parent',
2410+
placeholder: 'window-sidebar-item-placeholder',
2411+
tolerance: 'pointer',
2412+
helper: 'clone',
2413+
opacity: 0.8,
2414+
2415+
start: function(event, ui) {
2416+
// Add dragging class
2417+
ui.item.addClass('window-sidebar-item-dragging');
2418+
2419+
// Create placeholder styling
2420+
ui.placeholder.css({
2421+
'height': ui.item.height(),
2422+
'visibility': 'visible',
2423+
});
2424+
},
2425+
2426+
sort: function(event, ui) {
2427+
// Ensure the helper follows the cursor properly
2428+
ui.helper.css('pointer-events', 'none');
2429+
},
2430+
2431+
stop: function(event, ui) {
2432+
// Remove dragging class
2433+
ui.item.removeClass('window-sidebar-item-dragging');
2434+
2435+
// Get the new order
2436+
const newOrder = $sidebar.find('.window-sidebar-item').map(function() {
2437+
return {
2438+
path: $(this).attr('data-path'),
2439+
name: $(this).text().trim()
2440+
};
2441+
}).get();
2442+
2443+
// Save the new order
2444+
saveSidebarOrder(newOrder);
2445+
}
2446+
}).disableSelection(); // Prevent text selection while dragging
2447+
2448+
// Make the sortable operation more responsive
2449+
$sidebar.on('mousedown', '.window-sidebar-item', function(e) {
2450+
if (!$(this).hasClass('window-sidebar-title')) {
2451+
$(this).addClass('grabbing');
2452+
}
2453+
});
2454+
2455+
$sidebar.on('mouseup mouseleave', '.window-sidebar-item', function() {
2456+
$(this).removeClass('grabbing');
2457+
});
2458+
}
2459+
23722460
//set styles
23732461
$(el_window_body).css(options.body_css);
23742462

@@ -3575,4 +3663,50 @@ document.addEventListener('scroll', function (event) {
35753663
}
35763664
}, true);
35773665

3666+
// Function to save sidebar order to user preferences
3667+
async function saveSidebarOrder(order) {
3668+
try {
3669+
await puter.kv.set({
3670+
key: "sidebar_items",
3671+
value: JSON.stringify(order)
3672+
});
3673+
3674+
// Save to window object for quick access
3675+
window.sidebar_items = JSON.stringify(order);
3676+
} catch(err) {
3677+
console.error('Error saving sidebar order:', err);
3678+
}
3679+
}
3680+
3681+
// Function to load and apply saved sidebar order
3682+
async function loadSavedSidebarOrder(el_window) {
3683+
setTimeout(async () => {
3684+
try {
3685+
// Load saved sidebar order
3686+
let savedOrder = window.sidebar_items
3687+
3688+
// If not found in window object, try to get from KV
3689+
if(!savedOrder){
3690+
savedOrder = await puter.kv.get("sidebar_items");
3691+
}
3692+
3693+
// If found, apply the order
3694+
if (savedOrder && savedOrder !== 'null' && savedOrder !== 'undefined') {
3695+
const order = JSON.parse(savedOrder);
3696+
const $sidebar = $(el_window).find('.window-sidebar');
3697+
3698+
// Reorder items according to saved order
3699+
order.forEach(item => {
3700+
const $item = $sidebar.find(`.window-sidebar-item[data-path="${item.path}"]`);
3701+
if ($item.length) {
3702+
$item.appendTo($sidebar);
3703+
}
3704+
});
3705+
}
3706+
} catch(err) {
3707+
console.error('Error loading sidebar order:', err);
3708+
}
3709+
}, 1000);
3710+
}
3711+
35783712
export default UIWindow;

src/gui/src/css/style.css

+29-1
Original file line numberDiff line numberDiff line change
@@ -1242,7 +1242,7 @@ span.header-sort-icon img {
12421242
cursor: pointer;
12431243
}
12441244

1245-
.window-sidebar-item {
1245+
.window-sidebar-item, .window-sidebar-item.grabbing {
12461246
margin-bottom: 6px;
12471247
margin-top: 2px;
12481248
padding: 4px;
@@ -1262,6 +1262,34 @@ span.header-sort-icon img {
12621262
background-color: #fefeff;
12631263
}
12641264

1265+
.window-sidebar-item.grabbing, .window-sidebar-item.ui-sortable-helper{
1266+
background-color: none !important;
1267+
height: 23px !important;
1268+
/* width: 100% !important; */
1269+
}
1270+
.window-sidebar-item-placeholder{
1271+
height: 27px !important;
1272+
}
1273+
.window-sidebar-item {
1274+
cursor: pointer;
1275+
user-select: none;
1276+
}
1277+
.window-sidebar-item:not(.window-sidebar-title):hover {
1278+
cursor: grab;
1279+
}
1280+
.window-sidebar-item.grabbing {
1281+
cursor: grabbing !important;
1282+
}
1283+
.window-sidebar-item-dragging {
1284+
background-color: #f5f5f5 !important;
1285+
opacity: 0.8;
1286+
cursor: grabbing;
1287+
}
1288+
.ui-sortable-helper {
1289+
background: white !important;
1290+
box-shadow: 0 2px 8px rgba(0,0,0,0.1) !important;
1291+
}
1292+
12651293
.window-sidebar-item-icon {
12661294
width: 14px;
12671295
height: 14px;

src/gui/src/icons/sidebar-folder.svg

+8
Loading

0 commit comments

Comments
 (0)