mirror of
https://github.com/HeyPuter/puter.git
synced 2025-12-30 09:40:00 -06:00
Configurable taskbar position (#1372)
* feat(gui): Enhance taskbar functionality and positioning - Implement dynamic taskbar position management with options for left, bottom, and right placements. - Update desktop dimensions and window positioning based on taskbar position. - Add context menu options for changing taskbar position on desktop devices. - Ensure mobile devices always display the taskbar at the bottom. - Introduce CSS styles for taskbar positioning and adjust desktop padding accordingly. * feat(gui): Reinitialize tooltips based on taskbar position - Implement dynamic tooltip positioning for taskbar items based on the current taskbar location (left, right, bottom). - Ensure tooltips are destroyed and recreated when the taskbar position is updated, enhancing user experience and consistency. * feat(gui): Improve taskbar and tooltip positioning - Add support for dynamic tooltip positioning for the 'top' taskbar location. - Adjust CSS styles for arrow tooltips to enhance visual alignment and positioning. - Refine taskbar item positioning for left and right placements to ensure consistent appearance. * Improve popover positioning based on taskbar location - Improve `UIPopover` positioning logic to account for left and right taskbar placements. - Adjust Y position of popovers to ensure they appear correctly below toolbars for left/right taskbars. - Update CSS styles for arrow tooltips to enforce consistent positioning with !important declarations. * Refactor context menu positioning logic in UITaskbarItem - Introduce a helper function to dynamically calculate context menu position based on the taskbar's location (top, bottom, left, right). - Ensure context menus are positioned correctly relative to the taskbar item, improving usability across different taskbar placements. * Fix UITaskbar window height calculation for consistent positioning - Adjust the height calculation for windows in the UITaskbar to remove an unnecessary offset, ensuring accurate window sizing relative to the toolbar height. - This change improves the visual consistency of maximized windows across different taskbar placements. * Enhance window snapping and positioning based on taskbar location - Introduce a new function to calculate snap dimensions and positions dynamically based on the taskbar's location (left, right, bottom). - Update window snapping logic to ensure windows are positioned correctly relative to the taskbar, improving usability and visual consistency. - Adjust boundary checks for window placement to account for taskbar height and position, enhancing the overall user experience. * Update default taskbar position to 'left' and adjust CSS padding for desktop layout - Change the default taskbar position from 'bottom' to 'left' in UITaskbar. - Modify CSS to increase left padding for desktop taskbar positioning, enhancing layout consistency. * Improve CSS for desktop layout with height adjustments - Update CSS styles for input fields and desktop taskbar to set height to 100vh, ensuring consistent full-page layout across different screen sizes. - This change improves the visual consistency and usability of the interface. * Improve desktop selectable interactivity and taskbar tooltip behavior - Add functionality to mark the desktop as selectable active, improving user interaction with desktop elements. - Update tooltip display logic to only show when the desktop is not in a selectable state, enhancing usability. - Adjust CSS styles for desktop layout, ensuring consistent behavior and appearance when the desktop is active or inactive. * Update default taskbar position logic for first-time and existing users - Set the taskbar position to 'left' for first-time visitors and default to 'bottom' for returning users without a saved preference. - This change improves the user experience by providing a more intuitive initial layout for new users while maintaining consistency for existing users.
This commit is contained in:
@@ -748,6 +748,9 @@ async function UIDesktop(options) {
|
||||
// ---------------------------------------------------------------
|
||||
UITaskbar();
|
||||
|
||||
// Update desktop dimensions after taskbar is initialized with position
|
||||
window.update_desktop_dimensions_for_taskbar();
|
||||
|
||||
const el_desktop = document.querySelector('.desktop');
|
||||
|
||||
window.active_element = el_desktop;
|
||||
@@ -1100,6 +1103,9 @@ async function UIDesktop(options) {
|
||||
|
||||
selection.clearSelection();
|
||||
}
|
||||
|
||||
// mark desktop as selectable active
|
||||
$('.desktop').addClass('desktop-selectable-active');
|
||||
})
|
||||
.on('move', ({ store: { changed: { added, removed } }, event }) => {
|
||||
window.desktop_selectable_is_active = true;
|
||||
@@ -1125,6 +1131,7 @@ async function UIDesktop(options) {
|
||||
})
|
||||
.on('stop', evt => {
|
||||
window.desktop_selectable_is_active = false;
|
||||
$('.desktop').removeClass('desktop-selectable-active');
|
||||
});
|
||||
}
|
||||
// ----------------------------------------------------
|
||||
@@ -1755,31 +1762,66 @@ $(document).on('contextmenu taphold', '.taskbar', function (event) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// Get current taskbar position
|
||||
const currentPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
// Create base menu items
|
||||
let menuItems = [];
|
||||
|
||||
// Only show position submenu on desktop devices
|
||||
if (!isMobile.phone && !isMobile.tablet) {
|
||||
menuItems.push({
|
||||
html: "Position",
|
||||
items: [
|
||||
{
|
||||
html: "Left",
|
||||
checked: currentPosition === 'left',
|
||||
onClick: function() {
|
||||
window.update_taskbar_position('left');
|
||||
}
|
||||
},
|
||||
{
|
||||
html: "Bottom",
|
||||
checked: currentPosition === 'bottom',
|
||||
onClick: function() {
|
||||
window.update_taskbar_position('bottom');
|
||||
}
|
||||
},
|
||||
{
|
||||
html: "Right",
|
||||
checked: currentPosition === 'right',
|
||||
onClick: function() {
|
||||
window.update_taskbar_position('right');
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
menuItems.push('-'); // divider
|
||||
}
|
||||
|
||||
// Add the "Show open windows" option for all devices
|
||||
menuItems.push({
|
||||
html: "Show open windows",
|
||||
onClick: function () {
|
||||
$(`.window`).showWindow();
|
||||
}
|
||||
});
|
||||
|
||||
// Add the "Show the desktop" option for all devices
|
||||
menuItems.push({
|
||||
html: "Show the desktop",
|
||||
onClick: function () {
|
||||
$(`.window`).hideWindow();
|
||||
}
|
||||
});
|
||||
|
||||
UIContextMenu({
|
||||
parent_element: $('.taskbar'),
|
||||
items: [
|
||||
//--------------------------------------------------
|
||||
// Show open windows
|
||||
//--------------------------------------------------
|
||||
{
|
||||
html: "Show open windows",
|
||||
onClick: function () {
|
||||
$(`.window`).showWindow();
|
||||
}
|
||||
},
|
||||
//--------------------------------------------------
|
||||
// Show the desktop
|
||||
//--------------------------------------------------
|
||||
{
|
||||
html: "Show the desktop",
|
||||
onClick: function () {
|
||||
$(`.window`).hideWindow();
|
||||
}
|
||||
}
|
||||
]
|
||||
items: menuItems
|
||||
});
|
||||
return false;
|
||||
})
|
||||
});
|
||||
|
||||
// Toolbar context menu
|
||||
$(document).on('contextmenu taphold', '.toolbar', function (event) {
|
||||
|
||||
@@ -62,7 +62,19 @@ function UIPopover(options){
|
||||
// X position
|
||||
const popover_width = options.width ?? $(el_popover).width();
|
||||
if(options.center_horizontally){
|
||||
x_pos = window.innerWidth/2 - popover_width/2 - 15;
|
||||
// Check taskbar position to determine popover positioning
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
|
||||
if(taskbar_position === 'left'){
|
||||
// Position in top-left corner for left taskbar
|
||||
x_pos = window.taskbar_height + 10; // Just to the right of the taskbar
|
||||
}else if(taskbar_position === 'right'){
|
||||
// Position in top-right corner for right taskbar
|
||||
x_pos = window.innerWidth - popover_width - window.taskbar_height - 40; // Just to the left of the taskbar
|
||||
}else{
|
||||
// Default bottom taskbar behavior - center horizontally
|
||||
x_pos = window.innerWidth/2 - popover_width/2 - 15;
|
||||
}
|
||||
}else{
|
||||
if(options.position === 'bottom' || options.position === 'top')
|
||||
x_pos = options.left ?? ($(options.snapToElement).offset().left - (popover_width/ 2) + 10);
|
||||
@@ -73,7 +85,16 @@ function UIPopover(options){
|
||||
// Y position
|
||||
const popover_height = options.height ?? $(el_popover).height();
|
||||
if(options.center_horizontally){
|
||||
y_pos = options.top ?? (window.innerHeight - (window.taskbar_height + popover_height + 10));
|
||||
// Check taskbar position to determine popover positioning
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
|
||||
if(taskbar_position === 'left' || taskbar_position === 'right'){
|
||||
// Position at top for left/right taskbars
|
||||
y_pos = window.toolbar_height + 10; // Just below the toolbar
|
||||
}else{
|
||||
// Default bottom taskbar behavior - position above taskbar
|
||||
y_pos = options.top ?? (window.innerHeight - (window.taskbar_height + popover_height + 10));
|
||||
}
|
||||
}else{
|
||||
y_pos = options.top ?? ($(options.snapToElement).offset().top + $(options.snapToElement).height() + 5);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,30 @@ async function UITaskbar(options){
|
||||
options = options ?? {};
|
||||
options.content = options.content ?? '';
|
||||
|
||||
// if first visit ever, set taskbar position to left
|
||||
if(window.first_visit_ever){
|
||||
await puter.kv.set('taskbar_position', 'left');
|
||||
}
|
||||
|
||||
// Load taskbar position preference from storage
|
||||
let taskbar_position = await puter.kv.get('taskbar_position');
|
||||
// if this is not first visit, set taskbar position to bottom since it's from a user that
|
||||
// used puter before customizing taskbar position was added and the taskbar position was set to bottom
|
||||
if (!taskbar_position) {
|
||||
taskbar_position = 'bottom'; // default position
|
||||
await puter.kv.set('taskbar_position', taskbar_position);
|
||||
}
|
||||
|
||||
// Force bottom position on mobile devices
|
||||
if (isMobile.phone || isMobile.tablet) {
|
||||
taskbar_position = 'bottom';
|
||||
// Update the stored preference to bottom for mobile devices
|
||||
await puter.kv.set('taskbar_position', taskbar_position);
|
||||
}
|
||||
|
||||
// Set global taskbar position
|
||||
window.taskbar_position = taskbar_position;
|
||||
|
||||
// get launch apps
|
||||
$.ajax({
|
||||
url: window.api_origin + "/get-launch-apps?icon_size=64",
|
||||
@@ -42,10 +66,13 @@ async function UITaskbar(options){
|
||||
});
|
||||
|
||||
let h = '';
|
||||
h += `<div id="ui-taskbar_${window.global_element_id}" class="taskbar" style="height:${window.taskbar_height}px;">`;
|
||||
h += `<div id="ui-taskbar_${window.global_element_id}" class="taskbar taskbar-position-${taskbar_position}" style="height:${window.taskbar_height}px;">`;
|
||||
h += `<div class="taskbar-sortable" style="display: flex; justify-content: center; z-index: 99999;"></div>`;
|
||||
h += `</div>`;
|
||||
|
||||
if(taskbar_position === 'left' || taskbar_position === 'right'){
|
||||
$('.desktop').addClass(`desktop-taskbar-position-${taskbar_position}`);
|
||||
}
|
||||
|
||||
$('.desktop').append(h);
|
||||
|
||||
@@ -242,12 +269,15 @@ async function UITaskbar(options){
|
||||
window.make_taskbar_sortable();
|
||||
}
|
||||
|
||||
//-------------------------------------------
|
||||
// Taskbar is sortable
|
||||
//-------------------------------------------
|
||||
window.make_taskbar_sortable = function(){
|
||||
//-------------------------------------------
|
||||
// Taskbar is sortable
|
||||
//-------------------------------------------
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
const axis = position === 'bottom' ? 'x' : 'y';
|
||||
|
||||
$('.taskbar-sortable').sortable({
|
||||
axis: "x",
|
||||
axis: axis,
|
||||
items: '.taskbar-item-sortable:not(.has-open-contextmenu)',
|
||||
cancel: '.has-open-contextmenu',
|
||||
placeholder: "taskbar-item-sortable-placeholder",
|
||||
@@ -304,4 +334,171 @@ window.make_taskbar_sortable = function(){
|
||||
});
|
||||
}
|
||||
|
||||
// Function to update taskbar position
|
||||
window.update_taskbar_position = async function(new_position) {
|
||||
// Prevent position changes on mobile devices - always keep bottom
|
||||
if (isMobile.phone || isMobile.tablet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Valid positions
|
||||
const valid_positions = ['left', 'bottom', 'right'];
|
||||
|
||||
if (!valid_positions.includes(new_position)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the new position
|
||||
await puter.kv.set('taskbar_position', new_position);
|
||||
window.taskbar_position = new_position;
|
||||
|
||||
// Remove old position classes and add new one
|
||||
$('.taskbar').removeClass('taskbar-position-left taskbar-position-bottom taskbar-position-right');
|
||||
$('.taskbar').addClass(`taskbar-position-${new_position}`);
|
||||
|
||||
// update desktop class, if left or right, add `desktop-taskbar-position-left` or `desktop-taskbar-position-right`
|
||||
$('.desktop').removeClass('desktop-taskbar-position-left');
|
||||
$('.desktop').removeClass('desktop-taskbar-position-right');
|
||||
$('.desktop').addClass(`desktop-taskbar-position-${new_position}`);
|
||||
|
||||
// Update desktop height/width calculations based on new position
|
||||
window.update_desktop_dimensions_for_taskbar();
|
||||
|
||||
// Update window positions if needed (for maximized windows)
|
||||
$('.window[data-is_maximized="1"]').each(function() {
|
||||
const el_window = this;
|
||||
window.update_maximized_window_for_taskbar(el_window);
|
||||
});
|
||||
|
||||
// Re-initialize sortable with correct axis
|
||||
$('.taskbar-sortable').sortable('destroy');
|
||||
window.make_taskbar_sortable();
|
||||
|
||||
// Reinitialize all taskbar item tooltips with new position
|
||||
$('.taskbar-item').each(function() {
|
||||
const $item = $(this);
|
||||
// Destroy existing tooltip
|
||||
if ($item.data('ui-tooltip')) {
|
||||
$item.tooltip('destroy');
|
||||
}
|
||||
|
||||
// Helper function to get tooltip position based on taskbar position
|
||||
function getTooltipPosition() {
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if (taskbarPosition === 'bottom') {
|
||||
return {
|
||||
my: "center bottom-20",
|
||||
at: "center top"
|
||||
};
|
||||
} else if (taskbarPosition === 'top') {
|
||||
return {
|
||||
my: "center top+20",
|
||||
at: "center bottom"
|
||||
};
|
||||
} else if (taskbarPosition === 'left') {
|
||||
return {
|
||||
my: "left+20 center",
|
||||
at: "right center"
|
||||
};
|
||||
} else if (taskbarPosition === 'right') {
|
||||
return {
|
||||
my: "right-20 center",
|
||||
at: "left center"
|
||||
};
|
||||
}
|
||||
return {
|
||||
my: "center bottom-20",
|
||||
at: "center top"
|
||||
}; // fallback
|
||||
}
|
||||
|
||||
const tooltipPosition = getTooltipPosition();
|
||||
|
||||
// Reinitialize tooltip with new position
|
||||
$item.tooltip({
|
||||
items: ".taskbar:not(.children-have-open-contextmenu) .taskbar-item",
|
||||
position: {
|
||||
my: tooltipPosition.my,
|
||||
at: tooltipPosition.at,
|
||||
using: function( position, feedback ) {
|
||||
$( this ).css( position );
|
||||
$( "<div>" )
|
||||
.addClass( "arrow" )
|
||||
.addClass( feedback.vertical )
|
||||
.addClass( feedback.horizontal )
|
||||
.appendTo( this );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Function to update desktop dimensions based on taskbar position
|
||||
window.update_desktop_dimensions_for_taskbar = function() {
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
|
||||
if (position === 'bottom') {
|
||||
$('.desktop').css({
|
||||
'height': `calc(100vh - ${window.taskbar_height + window.toolbar_height}px)`,
|
||||
'width': '100%',
|
||||
'left': '0',
|
||||
'top': `${window.toolbar_height}px`
|
||||
});
|
||||
} else if (position === 'left') {
|
||||
$('.desktop').css({
|
||||
'height': `calc(100vh - ${window.toolbar_height}px)`,
|
||||
'width': `calc(100% - ${window.taskbar_height}px)`,
|
||||
'left': `${window.taskbar_height}px`,
|
||||
'top': `${window.toolbar_height}px`
|
||||
});
|
||||
} else if (position === 'right') {
|
||||
$('.desktop').css({
|
||||
'height': `calc(100vh - ${window.toolbar_height}px)`,
|
||||
'width': `calc(100% - ${window.taskbar_height}px)`,
|
||||
'left': '0',
|
||||
'top': `${window.toolbar_height}px`
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Function to update maximized window positioning based on taskbar position
|
||||
window.update_maximized_window_for_taskbar = function(el_window) {
|
||||
const position = window.taskbar_position || 'bottom';
|
||||
|
||||
// Handle fullpage mode differently
|
||||
if (window.is_fullpage_mode) {
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + 'px',
|
||||
'left': '0',
|
||||
'width': '100%',
|
||||
'height': `calc(100% - ${window.toolbar_height}px)`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (position === 'bottom') {
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + 'px',
|
||||
'left': '0',
|
||||
'width': '100%',
|
||||
'height': `calc(100% - ${window.taskbar_height + window.toolbar_height + 6}px)`,
|
||||
});
|
||||
} else if (position === 'left') {
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + 'px',
|
||||
'left': window.taskbar_height + 'px',
|
||||
'width': `calc(100% - ${window.taskbar_height}px)`,
|
||||
'height': `calc(100% - ${window.toolbar_height}px)`,
|
||||
});
|
||||
} else if (position === 'right') {
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + 'px',
|
||||
'left': '0',
|
||||
'width': `calc(100% - ${window.taskbar_height}px)`,
|
||||
'height': `calc(100% - ${window.toolbar_height}px)`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default UITaskbar;
|
||||
@@ -268,27 +268,92 @@ function UITaskbarItem(options){
|
||||
const pos = el_taskbar_item.getBoundingClientRect();
|
||||
UIContextMenu({
|
||||
parent_element: el_taskbar_item,
|
||||
position: {top: pos.top - 15, left: pos.left+5},
|
||||
position: getContextMenuPosition(pos),
|
||||
items: menu_items
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$( el_taskbar_item ).tooltip({
|
||||
items: ".taskbar:not(.children-have-open-contextmenu) .taskbar-item",
|
||||
position: {
|
||||
// Helper function to get tooltip position based on taskbar position
|
||||
function getTooltipPosition() {
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if (taskbarPosition === 'bottom') {
|
||||
return {
|
||||
my: "center bottom-20",
|
||||
at: "center top"
|
||||
};
|
||||
} else if (taskbarPosition === 'top') {
|
||||
return {
|
||||
my: "center top+20",
|
||||
at: "center bottom"
|
||||
};
|
||||
} else if (taskbarPosition === 'left') {
|
||||
return {
|
||||
my: "left+20 center",
|
||||
at: "right center"
|
||||
};
|
||||
} else if (taskbarPosition === 'right') {
|
||||
return {
|
||||
my: "right-20 center",
|
||||
at: "left center"
|
||||
};
|
||||
}
|
||||
return {
|
||||
my: "center bottom-20",
|
||||
at: "center top",
|
||||
at: "center top"
|
||||
}; // fallback
|
||||
}
|
||||
|
||||
// Helper function to get context menu position based on taskbar position
|
||||
function getContextMenuPosition(pos) {
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if (taskbarPosition === 'bottom') {
|
||||
return {
|
||||
top: pos.top - 15,
|
||||
left: pos.left + 5
|
||||
};
|
||||
} else if (taskbarPosition === 'top') {
|
||||
return {
|
||||
top: pos.bottom + 15,
|
||||
left: pos.left + 5
|
||||
};
|
||||
} else if (taskbarPosition === 'left') {
|
||||
return {
|
||||
top: pos.top + 5,
|
||||
left: pos.right + 5
|
||||
};
|
||||
} else if (taskbarPosition === 'right') {
|
||||
return {
|
||||
top: pos.top + 5,
|
||||
left: pos.left - 20
|
||||
};
|
||||
}
|
||||
return {
|
||||
top: pos.top - 15,
|
||||
left: pos.left + 5
|
||||
}; // fallback
|
||||
}
|
||||
|
||||
const tooltipPosition = getTooltipPosition();
|
||||
|
||||
$( el_taskbar_item ).tooltip({
|
||||
// only show tooltip if desktop is not selectable active
|
||||
items: ".desktop:not(.desktop-selectable-active) .taskbar:not(.children-have-open-contextmenu) .taskbar-item",
|
||||
position: {
|
||||
my: tooltipPosition.my,
|
||||
at: tooltipPosition.at,
|
||||
using: function( position, feedback ) {
|
||||
$( this ).css( position );
|
||||
$( "<div>" )
|
||||
.addClass( "arrow" )
|
||||
.addClass( feedback.vertical )
|
||||
.addClass( feedback.horizontal )
|
||||
.appendTo( this );
|
||||
$( this ).css( position );
|
||||
$( "<div>" )
|
||||
.addClass( "arrow" )
|
||||
.addClass( feedback.vertical )
|
||||
.addClass( feedback.horizontal )
|
||||
.appendTo( this );
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// --------------------------------------------------------
|
||||
|
||||
@@ -34,6 +34,37 @@ import item_icon from '../helpers/item_icon.js';
|
||||
|
||||
const el_body = document.getElementsByTagName('body')[0];
|
||||
|
||||
// Function to get snap dimensions and positions based on taskbar position
|
||||
function getSnapDimensions() {
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
|
||||
let available_width, available_height, start_x, start_y;
|
||||
|
||||
if (taskbar_position === 'left') {
|
||||
available_width = window.innerWidth - window.taskbar_height;
|
||||
available_height = window.innerHeight - window.toolbar_height;
|
||||
start_x = window.taskbar_height;
|
||||
start_y = window.toolbar_height;
|
||||
} else if (taskbar_position === 'right') {
|
||||
available_width = window.innerWidth - window.taskbar_height;
|
||||
available_height = window.innerHeight - window.toolbar_height;
|
||||
start_x = 0;
|
||||
start_y = window.toolbar_height;
|
||||
} else { // bottom (default)
|
||||
available_width = window.innerWidth;
|
||||
available_height = window.innerHeight - window.toolbar_height - window.taskbar_height;
|
||||
start_x = 0;
|
||||
start_y = window.toolbar_height;
|
||||
}
|
||||
|
||||
return {
|
||||
available_width,
|
||||
available_height,
|
||||
start_x,
|
||||
start_y
|
||||
};
|
||||
}
|
||||
|
||||
async function UIWindow(options) {
|
||||
const win_id = window.global_element_id++;
|
||||
window.last_window_zindex++;
|
||||
@@ -549,14 +580,8 @@ async function UIWindow(options) {
|
||||
// shrink icon
|
||||
$(el_window).find('.window-scale-btn>img').attr('src', window.icons['scale-down-3.svg']);
|
||||
|
||||
// set new size and position
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + 'px',
|
||||
'left': '0',
|
||||
'width': '100%',
|
||||
'height': `calc(100% - ${window.taskbar_height + window.toolbar_height + 6}px)`,
|
||||
'transform': 'none',
|
||||
});
|
||||
// Use taskbar position-aware window positioning
|
||||
window.update_maximized_window_for_taskbar(el_window);
|
||||
}
|
||||
|
||||
// when a window is created, focus is brought to it and
|
||||
@@ -1701,14 +1726,17 @@ async function UIWindow(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get taskbar-aware snap dimensions
|
||||
const snapDims = getSnapDimensions();
|
||||
|
||||
// W
|
||||
if(!window_is_snapped && window.current_active_snap_zone === 'w'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'width': '50%',
|
||||
'height': window.desktop_height,
|
||||
'top': window.toolbar_height,
|
||||
'left': 0,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1716,10 +1744,10 @@ async function UIWindow(options) {
|
||||
else if(!window_is_snapped && window.current_active_snap_zone === 'nw'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': window.toolbar_height,
|
||||
'left': 0,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1727,10 +1755,10 @@ async function UIWindow(options) {
|
||||
else if(!window_is_snapped && window.current_active_snap_zone ==='ne'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': window.toolbar_height,
|
||||
'left': window.desktop_width/2,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x + snapDims.available_width / 2,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1738,11 +1766,10 @@ async function UIWindow(options) {
|
||||
else if(!window_is_snapped && window.current_active_snap_zone ==='e'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'width': '50%',
|
||||
'height': window.desktop_height,
|
||||
'top': window.toolbar_height,
|
||||
'left': 'initial',
|
||||
'right': 0,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x + snapDims.available_width / 2,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1750,10 +1777,10 @@ async function UIWindow(options) {
|
||||
else if(!window_is_snapped && window.current_active_snap_zone ==='n'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'width': window.desktop_width,
|
||||
'height': window.desktop_height,
|
||||
'top': window.toolbar_height,
|
||||
'left': 0,
|
||||
'width': snapDims.available_width,
|
||||
'height': snapDims.available_height,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1761,10 +1788,10 @@ async function UIWindow(options) {
|
||||
else if(!window_is_snapped && window.current_active_snap_zone ==='sw'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'top': window.toolbar_height + window.desktop_height/2,
|
||||
'left': 0,
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': snapDims.start_y + snapDims.available_height / 2,
|
||||
'left': snapDims.start_x,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1772,10 +1799,10 @@ async function UIWindow(options) {
|
||||
else if(!window_is_snapped && window.current_active_snap_zone ==='se'){
|
||||
window_snap_placeholder.css({
|
||||
'display': 'block',
|
||||
'top': window.toolbar_height + window.desktop_height/2,
|
||||
'left': window.desktop_width/2,
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': snapDims.start_y + snapDims.available_height / 2,
|
||||
'left': snapDims.start_x + snapDims.available_width / 2,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
'z-index': window.last_window_zindex - 1,
|
||||
})
|
||||
}
|
||||
@@ -1823,58 +1850,61 @@ async function UIWindow(options) {
|
||||
$(window_snap_placeholder).css('padding', 0);
|
||||
|
||||
setTimeout(function(){
|
||||
// Get taskbar-aware snap dimensions for final positioning
|
||||
const snapDims = getSnapDimensions();
|
||||
|
||||
// snap to w
|
||||
if(window.current_active_snap_zone === 'w'){
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height,
|
||||
'left': 0,
|
||||
'width': '50%',
|
||||
'height': window.desktop_height - 6,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height - 6,
|
||||
})
|
||||
}
|
||||
// snap to nw
|
||||
else if(window.current_active_snap_zone === 'nw'){
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height,
|
||||
'left': 0,
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
})
|
||||
}
|
||||
// snap to ne
|
||||
else if(window.current_active_snap_zone === 'ne'){
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height,
|
||||
'left': '50%',
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x + snapDims.available_width / 2,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
})
|
||||
}
|
||||
// snap to sw
|
||||
else if(window.current_active_snap_zone === 'sw'){
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + window.desktop_height/2,
|
||||
'left': 0,
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': snapDims.start_y + snapDims.available_height / 2,
|
||||
'left': snapDims.start_x,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
})
|
||||
}
|
||||
// snap to se
|
||||
else if(window.current_active_snap_zone === 'se'){
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height + window.desktop_height/2,
|
||||
'left': window.desktop_width/2,
|
||||
'width': '50%',
|
||||
'height': window.desktop_height/2,
|
||||
'top': snapDims.start_y + snapDims.available_height / 2,
|
||||
'left': snapDims.start_x + snapDims.available_width / 2,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height / 2,
|
||||
})
|
||||
}
|
||||
// snap to e
|
||||
else if(window.current_active_snap_zone === 'e'){
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height,
|
||||
'left': '50%',
|
||||
'width': '50%',
|
||||
'height': window.desktop_height - 6,
|
||||
'top': snapDims.start_y,
|
||||
'left': snapDims.start_x + snapDims.available_width / 2,
|
||||
'width': snapDims.available_width / 2,
|
||||
'height': snapDims.available_height - 6,
|
||||
})
|
||||
}
|
||||
// snap to n
|
||||
@@ -1906,23 +1936,43 @@ async function UIWindow(options) {
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// if window is dropped below the taskbar, move it up
|
||||
// if window is dropped outside the available area, move it back in
|
||||
// Bottom boundary (account for taskbar position)
|
||||
const taskbar_position = window.taskbar_position || 'bottom';
|
||||
let maxTop;
|
||||
if(taskbar_position === 'bottom'){
|
||||
maxTop = window.innerHeight - window.taskbar_height - 30;
|
||||
} else {
|
||||
maxTop = window.innerHeight - 30;
|
||||
}
|
||||
// the lst '- 30' is to account for the window head
|
||||
if($(el_window).position().top > window.innerHeight - window.taskbar_height - 30 && !window_will_snap){
|
||||
if($(el_window).position().top > maxTop && !window_will_snap){
|
||||
$(el_window).animate({
|
||||
top: window.innerHeight - window.taskbar_height - 60,
|
||||
top: maxTop - 30,
|
||||
}, 100);
|
||||
}
|
||||
// if window is dropped too far to the right, move it left
|
||||
if($(el_window).position().left > window.innerWidth - 50 && !window_will_snap){
|
||||
let maxLeft;
|
||||
if(taskbar_position === 'right'){
|
||||
maxLeft = window.innerWidth - window.taskbar_height - 50;
|
||||
} else {
|
||||
maxLeft = window.innerWidth - 50;
|
||||
}
|
||||
if($(el_window).position().left > maxLeft && !window_will_snap){
|
||||
$(el_window).animate({
|
||||
left: window.innerWidth - 50,
|
||||
left: maxLeft,
|
||||
}, 100);
|
||||
}
|
||||
// if window is dropped too far to the left, move it right
|
||||
if(($(el_window).position().left + $(el_window).width() - 150 )< 0 && !window_will_snap){
|
||||
let minLeft;
|
||||
if(taskbar_position === 'left'){
|
||||
minLeft = window.taskbar_height - $(el_window).width() + 150;
|
||||
} else {
|
||||
minLeft = -$(el_window).width() + 150;
|
||||
}
|
||||
if($(el_window).position().left < minLeft && !window_will_snap){
|
||||
$(el_window).animate({
|
||||
left: -1 * ($(el_window).width() - 150),
|
||||
left: minLeft,
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
@@ -3296,22 +3346,8 @@ window.scale_window = (el_window)=>{
|
||||
// shrink icon
|
||||
$(el_window).find('.window-scale-btn>img').attr('src', window.icons['scale-down-3.svg']);
|
||||
|
||||
// calculate height
|
||||
let height;
|
||||
if(window.is_fullpage_mode){
|
||||
height = `calc(100% - ${ window.toolbar_height}px)`;
|
||||
}else{
|
||||
height = `calc(100% - ${window.taskbar_height + window.toolbar_height + 6}px)`;
|
||||
}
|
||||
|
||||
// set new size and position
|
||||
$(el_window).css({
|
||||
'top': window.toolbar_height+'px',
|
||||
'left': '0',
|
||||
'width': '100%',
|
||||
'height': height,
|
||||
'transform': 'none',
|
||||
});
|
||||
// Use taskbar position-aware window positioning
|
||||
window.update_maximized_window_for_taskbar(el_window);
|
||||
|
||||
// hide toolbar
|
||||
if(!isMobile.phone && !isMobile.tablet){
|
||||
@@ -3562,9 +3598,29 @@ $.fn.hideWindow = async function(options) {
|
||||
if($(this).hasClass('window')){
|
||||
// get taskbar item location
|
||||
let taskbar_item_pos = $(`.taskbar .taskbar-item[data-app="${$(this).attr('data-app')}"]`).position();
|
||||
|
||||
// taskbar position is center of window minus half of taskbar item width
|
||||
taskbar_item_pos.left = taskbar_item_pos.left + ($( window ).width()/ 2) - ($(`.taskbar`).width() / 2);
|
||||
|
||||
// Calculate animation target based on taskbar position
|
||||
let animationTarget = {};
|
||||
const taskbarPosition = window.taskbar_position || 'bottom';
|
||||
|
||||
if (taskbarPosition === 'bottom') {
|
||||
// taskbar position is center of window minus half of taskbar item width
|
||||
taskbar_item_pos.left = taskbar_item_pos.left + ($( window ).width()/ 2) - ($(`.taskbar`).width() / 2);
|
||||
animationTarget = {
|
||||
top: 'calc(100% - 60px)',
|
||||
left: taskbar_item_pos.left + 14.5,
|
||||
};
|
||||
} else if (taskbarPosition === 'left') {
|
||||
animationTarget = {
|
||||
top: taskbar_item_pos.top + ($( window ).height()/ 2) - ($(`.taskbar`).height() / 2) + 14.5,
|
||||
left: '5px',
|
||||
};
|
||||
} else if (taskbarPosition === 'right') {
|
||||
animationTarget = {
|
||||
top: taskbar_item_pos.top + ($( window ).height()/ 2) - ($(`.taskbar`).height() / 2) + 14.5,
|
||||
left: 'calc(100% - 60px)',
|
||||
};
|
||||
}
|
||||
|
||||
$(this).attr({
|
||||
'data-orig-width': $(this).width(),
|
||||
@@ -3580,8 +3636,7 @@ $.fn.hideWindow = async function(options) {
|
||||
} : {}),
|
||||
width: `0`,
|
||||
height: `0`,
|
||||
top: 'calc(100% - 60px)',
|
||||
left: taskbar_item_pos.left + 14.5,
|
||||
...animationTarget,
|
||||
});
|
||||
|
||||
// remove transitions a good while after setting css to make sure
|
||||
|
||||
@@ -340,7 +340,7 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, sel
|
||||
.desktop {
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
height: calc(100vh - 60px);
|
||||
height: calc(100vh - 60px) !important;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: repeat(auto-fill, 109px);
|
||||
@@ -349,6 +349,24 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, sel
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.device-desktop .desktop {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.desktop.desktop-taskbar-position-left {
|
||||
margin-left: 50px;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
height: calc(100vh) !important;
|
||||
}
|
||||
|
||||
.desktop.desktop-taskbar-position-right {
|
||||
margin-right: 50px;
|
||||
padding-left: 0;
|
||||
padding-bottom: 0;
|
||||
height: calc(100vh) !important;
|
||||
}
|
||||
|
||||
.fullpage-mode .window-minimize-btn {
|
||||
display: none;
|
||||
}
|
||||
@@ -356,6 +374,10 @@ input[type=text]:focus, input[type=password]:focus, input[type=email]:focus, sel
|
||||
.device-phone .desktop {
|
||||
height: calc(100vh - 90px) !important;
|
||||
height: calc(100dvh - 90px) !important;
|
||||
/* Ensure no left/right padding on mobile, regardless of taskbar position classes */
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.item-container-list {
|
||||
@@ -2355,6 +2377,74 @@ label {
|
||||
0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
/* Bottom positioned taskbar (default) */
|
||||
.taskbar.taskbar-position-bottom {
|
||||
bottom: 5px;
|
||||
left: 50%;
|
||||
right: auto;
|
||||
top: auto;
|
||||
width: auto;
|
||||
height: 50px;
|
||||
transform: translateX(-50%);
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
writing-mode: initial;
|
||||
}
|
||||
|
||||
/* Left positioned taskbar */
|
||||
.taskbar.taskbar-position-left {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 50px;
|
||||
transform: none;
|
||||
height: 100% !important;
|
||||
flex-direction: column;
|
||||
justify-content: normal;
|
||||
writing-mode: initial;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
/* Right positioned taskbar */
|
||||
.taskbar.taskbar-position-right {
|
||||
right: 0;
|
||||
top: 0;
|
||||
left: auto;
|
||||
bottom: auto;
|
||||
width: 50px;
|
||||
height: 100% !important;
|
||||
transform: none;
|
||||
flex-direction: column;
|
||||
justify-content: normal;
|
||||
writing-mode: initial;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-left .taskbar-sortable,
|
||||
.taskbar.taskbar-position-right .taskbar-sortable {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Taskbar items for left/right positioning */
|
||||
.taskbar.taskbar-position-left .taskbar-item,
|
||||
.taskbar.taskbar-position-right .taskbar-item {
|
||||
margin-bottom: 5px;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.taskbar.taskbar-position-left .taskbar-item:last-child,
|
||||
.taskbar.taskbar-position-right .taskbar-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item {
|
||||
float: left;
|
||||
position: relative;
|
||||
@@ -2386,7 +2476,7 @@ label {
|
||||
z-index: 999999999 !important;
|
||||
}
|
||||
|
||||
.taskbar .taskbar-item:hover .taskbar-icon {
|
||||
.desktop:not(.desktop-selectable-active) .taskbar .taskbar-item:hover .taskbar-icon {
|
||||
background-color: rgb(255 255 255 / 40%);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
@@ -2546,6 +2636,7 @@ label {
|
||||
filter: drop-shadow(0 0 3px rgba(0, 0, 0, .455));
|
||||
}
|
||||
|
||||
/* Base arrow styles */
|
||||
.arrow {
|
||||
width: 70px;
|
||||
height: 16px;
|
||||
@@ -2557,15 +2648,6 @@ label {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.arrow.top {
|
||||
top: -16px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
.arrow.left {
|
||||
left: 20%;
|
||||
}
|
||||
|
||||
.arrow:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -2576,11 +2658,94 @@ label {
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
background-color: rgba(231, 238, 245, .92);
|
||||
}
|
||||
|
||||
/* Arrow pointing up (tooltip below taskbar item) */
|
||||
.arrow.bottom {
|
||||
bottom: auto;
|
||||
top: 31px !important;
|
||||
transform: scaleY(-1);
|
||||
left: calc(50% + 2px) !important;
|
||||
}
|
||||
|
||||
.arrow.bottom:after {
|
||||
bottom: -20px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
/* Arrow pointing down (tooltip above taskbar item) */
|
||||
.arrow.top {
|
||||
top: auto;
|
||||
bottom: -16px;
|
||||
}
|
||||
|
||||
.arrow.top:after {
|
||||
bottom: -20px;
|
||||
top: auto;
|
||||
top: -20px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Arrow pointing right (tooltip to the right of taskbar item) */
|
||||
.arrow.left {
|
||||
width: 16px;
|
||||
height: 70px;
|
||||
left: -16px;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
margin-left: 0;
|
||||
margin-top: -37px;
|
||||
transform: scaleX(-1);
|
||||
top:18px !important;
|
||||
}
|
||||
|
||||
.arrow.left:after {
|
||||
left: -20px;
|
||||
top: 20px;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Arrow pointing left (tooltip to the left of taskbar item) */
|
||||
.arrow.right {
|
||||
width: 16px;
|
||||
height: 70px;
|
||||
right: -16px !important;
|
||||
left: auto;
|
||||
margin-left: 0;
|
||||
margin-top: 35px;
|
||||
transform: scaleX(-1);
|
||||
position: absolute;
|
||||
top:18px !important;
|
||||
}
|
||||
|
||||
.arrow.right:after {
|
||||
right: -20px !important;
|
||||
left: auto !important;
|
||||
top: 20px;
|
||||
bottom: auto;
|
||||
}
|
||||
|
||||
/* Center positioning adjustments */
|
||||
.arrow.center {
|
||||
left: 50%;
|
||||
margin-left: -35px;
|
||||
}
|
||||
|
||||
.arrow.middle {
|
||||
top: 50%;
|
||||
margin-top: -35px;
|
||||
}
|
||||
|
||||
/* Horizontal center adjustments for left/right arrows */
|
||||
.arrow.left.middle,
|
||||
.arrow.right.middle {
|
||||
margin-top: -35px;
|
||||
}
|
||||
|
||||
/* Vertical center adjustments for top/bottom arrows */
|
||||
.arrow.top.center,
|
||||
.arrow.bottom.center {
|
||||
margin-left: -35px;
|
||||
}
|
||||
|
||||
/******************************************************/
|
||||
@@ -4599,6 +4764,21 @@ fieldset[name=number-code] {
|
||||
|
||||
/* Taskbar container */
|
||||
.device-phone .taskbar {
|
||||
/* Force taskbar to bottom on mobile devices, overriding any position classes */
|
||||
position: fixed !important;
|
||||
bottom: 5px !important;
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
top: auto !important;
|
||||
width: auto !important;
|
||||
height: 50px !important;
|
||||
transform: translateX(-50%) !important;
|
||||
flex-direction: row !important;
|
||||
justify-content: left !important;
|
||||
writing-mode: initial !important;
|
||||
padding: 0 7px !important;
|
||||
border-radius: 10px !important;
|
||||
|
||||
/* Enable smooth scrolling */
|
||||
-webkit-overflow-scrolling: touch;
|
||||
/* Allow horizontal touch scrolling */
|
||||
@@ -4611,7 +4791,6 @@ fieldset[name=number-code] {
|
||||
|
||||
/* Base styling */
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
/* Hide scrollbar while keeping functionality */
|
||||
|
||||
Reference in New Issue
Block a user