Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 60 additions & 7 deletions packages/node_modules/@node-red/editor-client/src/js/ui/palette.js
Original file line number Diff line number Diff line change
Expand Up @@ -674,14 +674,58 @@ RED.palette = (function() {
RED.popover.tooltip(sidebarControls,RED._("keyboard.togglePalette"),"core:toggle-palette");

sidebarControls.on("click", function() {
// On mobile, don't hide the button after clicking
if (!RED.utils.isMobileDevice()) {
sidebarControls.hide();
}
RED.menu.toggleSelected("menu-item-palette");
})
$("#red-ui-palette").on("mouseenter", function() {
sidebarControls.toggle("slide", { direction: "left" }, 200);
})
$("#red-ui-palette").on("mouseleave", function() {
sidebarControls.stop(false,true);
sidebarControls.hide();

// Check if mobile device
var isMobile = RED.utils.isMobileDevice();

if (isMobile) {
// On mobile devices, always show the toggle button
sidebarControls.show();
// Update the icon based on palette state
if (RED.menu.isSelected("menu-item-palette")) {
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
} else {
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
}
} else {
// On desktop, use hover behavior
$("#red-ui-palette").on("mouseenter", function() {
sidebarControls.toggle("slide", { direction: "left" }, 200);
})
$("#red-ui-palette").on("mouseleave", function() {
sidebarControls.stop(false,true);
sidebarControls.hide();
})
}

// Handle window resize to update mobile state
$(window).on("resize", function() {
var wasMobile = isMobile;
isMobile = RED.utils.isMobileDevice();

if (wasMobile !== isMobile) {
if (isMobile) {
// Switching to mobile - show button and remove hover events
$("#red-ui-palette").off("mouseenter mouseleave");
sidebarControls.show();
} else {
// Switching to desktop - hide button and add hover events
sidebarControls.hide();
$("#red-ui-palette").on("mouseenter", function() {
sidebarControls.toggle("slide", { direction: "left" }, 200);
})
$("#red-ui-palette").on("mouseleave", function() {
sidebarControls.stop(false,true);
sidebarControls.hide();
})
}
}
})
var userCategories = [];
if (RED.settings.paletteCategories) {
Expand Down Expand Up @@ -754,12 +798,21 @@ RED.palette = (function() {
function togglePalette(state) {
if (!state) {
$("#red-ui-main-container").addClass("red-ui-palette-closed");
sidebarControls.hide();
// Only hide on desktop
if (!RED.utils.isMobileDevice()) {
sidebarControls.hide();
}
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
} else {
$("#red-ui-main-container").removeClass("red-ui-palette-closed");
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
}

// On mobile, always keep the button visible
if (RED.utils.isMobileDevice()) {
sidebarControls.show();
}

setTimeout(function() { $(window).trigger("resize"); } ,200);
}

Expand Down
120 changes: 105 additions & 15 deletions packages/node_modules/@node-red/editor-client/src/js/ui/sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,75 @@ RED.sidebar = (function() {

var sidebarControls = $('<div class="red-ui-sidebar-control-right"><i class="fa fa-chevron-right"</div>').appendTo($("#red-ui-sidebar-separator"));
sidebarControls.on("click", function() {
sidebarControls.hide();
// On mobile, don't hide the button after clicking
if (!RED.utils.isMobileDevice()) {
sidebarControls.hide();
}
RED.menu.toggleSelected("menu-item-sidebar");
})
$("#red-ui-sidebar-separator").on("mouseenter", function() {
if (!sidebarSeparator.dragging) {
if (RED.menu.isSelected("menu-item-sidebar")) {
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");

// Check if mobile device
var isMobile = RED.utils.isMobileDevice();

if (isMobile) {
// On mobile devices, always show the toggle button
sidebarControls.show();
// Update the icon based on sidebar state
if (RED.menu.isSelected("menu-item-sidebar")) {
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
} else {
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
}
} else {
// On desktop, use hover behavior
$("#red-ui-sidebar-separator").on("mouseenter", function() {
if (!sidebarSeparator.dragging) {
if (RED.menu.isSelected("menu-item-sidebar")) {
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
} else {
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
}
sidebarControls.toggle("slide", { direction: "right" }, 200);
}
})
$("#red-ui-sidebar-separator").on("mouseleave", function() {
if (!sidebarSeparator.dragging) {
sidebarControls.stop(false,true);
sidebarControls.hide();
}
})
}

// Handle window resize to update mobile state
$(window).on("resize", function() {
var wasMobile = isMobile;
isMobile = RED.utils.isMobileDevice();

if (wasMobile !== isMobile) {
if (isMobile) {
// Switching to mobile - show button and remove hover events
$("#red-ui-sidebar-separator").off("mouseenter mouseleave");
sidebarControls.show();
} else {
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
// Switching to desktop - hide button and add hover events
sidebarControls.hide();
$("#red-ui-sidebar-separator").on("mouseenter", function() {
if (!sidebarSeparator.dragging) {
if (RED.menu.isSelected("menu-item-sidebar")) {
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
} else {
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
}
sidebarControls.toggle("slide", { direction: "right" }, 200);
}
})
$("#red-ui-sidebar-separator").on("mouseleave", function() {
if (!sidebarSeparator.dragging) {
sidebarControls.stop(false,true);
sidebarControls.hide();
}
})
}
sidebarControls.toggle("slide", { direction: "right" }, 200);
}
})
$("#red-ui-sidebar-separator").on("mouseleave", function() {
if (!sidebarSeparator.dragging) {
sidebarControls.stop(false,true);
sidebarControls.hide();
}
});
}
Expand All @@ -197,16 +249,43 @@ RED.sidebar = (function() {
if (!state) {
$("#red-ui-main-container").addClass("red-ui-sidebar-closed");
} else {
// On mobile, close palette if opening sidebar
if (RED.utils.isMobileDevice()) {
$("#red-ui-main-container").addClass("red-ui-palette-closed");
if (RED.menu && RED.menu.isSelected("menu-item-palette")) {
RED.menu.setSelected("menu-item-palette", false);
}
}
$("#red-ui-main-container").removeClass("red-ui-sidebar-closed");
sidebar_tabs.resize();
}

// Update toggle button icon on mobile devices
if (RED.utils.isMobileDevice()) {
var sidebarControls = $(".red-ui-sidebar-control-right");
if (state) {
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
} else {
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
}
}

RED.events.emit("sidebar:resize");
}

function showSidebar(id, skipShowSidebar) {
if (id === ":first") {
id = lastSessionSelectedTab || RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])[0]
}

// On mobile, close palette if opening sidebar
if (RED.utils.isMobileDevice() && !skipShowSidebar) {
$("#red-ui-main-container").addClass("red-ui-palette-closed");
if (RED.menu && RED.menu.isSelected("menu-item-palette")) {
RED.menu.setSelected("menu-item-palette", false);
}
}

if (id) {
if (!containsTab(id) && knownTabs[id]) {
sidebar_tabs.addTab(knownTabs[id]);
Expand All @@ -224,6 +303,15 @@ RED.sidebar = (function() {

function init () {
setupSidebarSeparator();

// Check if mobile and collapse by default
if (RED.utils.isMobileDevice()) {
$("#red-ui-sidebar").addClass("closed");
$("#red-ui-main-container").addClass("red-ui-sidebar-closed");
sidebarSeparator.opening = false;
sidebarSeparator.width = 0;
}

sidebar_tabs = RED.tabs.create({
element: $('<ul id="red-ui-sidebar-tabs"></ul>').appendTo("#red-ui-sidebar"),
onchange:function(tab) {
Expand Down Expand Up @@ -272,8 +360,10 @@ RED.sidebar = (function() {
RED.sidebar.help.init();
RED.sidebar.config.init();
RED.sidebar.context.init();
// hide info bar at start if screen rather narrow...
if ($("#red-ui-editor").width() < 600) { RED.menu.setSelected("menu-item-sidebar",false); }
// hide info bar at start if screen rather narrow or on mobile device
if ($("#red-ui-editor").width() < 600 || RED.utils.isMobileDevice()) {
RED.menu.setSelected("menu-item-sidebar",false);
}
}

return {
Expand Down
12 changes: 12 additions & 0 deletions packages/node_modules/@node-red/editor-client/src/js/ui/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,17 @@ RED.utils = (function() {
return r;
}

function isMobileDevice() {
// Check for mobile devices using both viewport width and touch capability
var viewportWidth = window.innerWidth || document.documentElement.clientWidth;
var browserInfo = getBrowserInfo();

// Consider device as mobile if:
// 1. Viewport width is less than 768px OR
// 2. Device has touch capability AND (is identified as mobile OR tablet)
return viewportWidth < 768 || (browserInfo.touch && (browserInfo.mobile || browserInfo.tablet));
}

return {
createObjectElement: createObjectElement,
getMessageProperty: getMessageProperty,
Expand All @@ -1538,6 +1549,7 @@ RED.utils = (function() {
parseModuleList: parseModuleList,
checkModuleAllowed: checkModuleAllowed,
getBrowserInfo: getBrowserInfo,
isMobileDevice: isMobileDevice,
validateTypedProperty: validateTypedProperty
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,38 @@ button.red-ui-sidebar-header-button-toggle {
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}

// Mobile-specific styles for sidebar toggles
@media (max-width: 768px) {
.red-ui-sidebar-control-right,
.red-ui-sidebar-control-left {
display: block !important; // Always visible on mobile
opacity: 0.8; // Slightly transparent to be less obtrusive
// Use same visual size as desktop by keeping desktop padding
// Touch target will still be maintained via transparent borders/margins
padding: 15px 8px; // Same padding as desktop
}

.red-ui-sidebar-control-right:active,
.red-ui-sidebar-control-left:active {
opacity: 1;
background: var(--red-ui-secondary-background);
}
}

// For touch devices regardless of viewport width
@media (hover: none) and (pointer: coarse) {
.red-ui-sidebar-control-right,
.red-ui-sidebar-control-left {
display: block !important; // Always visible on touch devices
opacity: 0.8;
// Use same visual size as desktop by keeping desktop padding
padding: 15px 8px; // Same padding as desktop
}

.red-ui-sidebar-control-right:active,
.red-ui-sidebar-control-left:active {
opacity: 1;
background: var(--red-ui-secondary-background);
}
}
Loading