Started work on userlist profile tooltips and context menus.

This commit is contained in:
rainbow napkin 2025-01-07 01:42:54 -05:00
parent 9df7f52e9e
commit 9a8def18d7
16 changed files with 372 additions and 22 deletions

View file

@ -187,6 +187,7 @@ p.panel-head-element{
span.user-entry{
display: flex;
margin-bottom: 0.1em;
cursor: pointer;
}
.user-entry{
@ -238,6 +239,7 @@ span.user-entry{
left: 0;
overflow-y: auto;
scrollbar-width: thin;
width: 75%;
}
#cpanel-pinned-div{

View file

@ -161,8 +161,25 @@ div.tooltip{
min-width: 1em;
min-height: 1em;
padding: 0.5em;
z-index: 20;
}
p.tooltip, h3.tooltip{
margin: 0 auto;
}
/* context menu */
.context-menu{
display: flex;
flex-direction: column;
align-items: stretch;
}
.context-menu h2{
margin: 0;
text-align: center;
}
.context-menu button{
margin: 0.05em 0;
}

47
www/css/panel/profile.css Normal file
View file

@ -0,0 +1,47 @@
/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
#profile-panel{
display: flex;
flex-direction: column;
}
.panel.profile-link{
text-align: right;
}
.panel.profile-name{
text-align: center;
}
.panel.profile-img{
margin: 0 2em;
max-height: 20em;
object-fit: contain;
}
p.panel{
margin: 0.2em 2em;
text-wrap: nowrap;
}
.panel.profile-bio-label{
margin-bottom: 0;
}
.panel.profile-bio{
margin-top: 0;
text-wrap: wrap;
}

View file

@ -0,0 +1,38 @@
/*Canopy - The next generation of stoner streaming software
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
div.tooltip{
display: flex;
flex-direction: column;
align-items: center;
}
.tooltip .profile-img{
max-width: 6em;
max-height: 6em;
object-fit: contain;
}
.tooltip .profile-name{
text-align: center;
margin: 0;
text-wrap: nowrap;
}
.tooltip p{
text-align: left;
width: 100%;
text-wrap: nowrap;
}

View file

@ -307,20 +307,7 @@ class adminUserList{
//Splice username out of class name
const name = userName.id.replace('admin-user-list-name-','');
//When the mouse starts to hover
userName.addEventListener('mouseenter',(event)=>{
//Create the tooltip
const tooltip = new canopyUXUtils.tooltip(`altList?user=${name}`, true);
//Do intial mouse move
tooltip.moveToMouse(event);
//Move the tooltip with the mouse
userName.addEventListener('mousemove', tooltip.moveToMouse.bind(tooltip));
//remove the tooltip on mouseleave
userName.addEventListener('mouseleave', tooltip.remove.bind(tooltip));
});
userName.addEventListener('mouseenter',(event)=>{utils.ux.displayTooltip(event, `altList?user=${name}`, true);});
}
for(let rankSelector of this.rankSelectors){

View file

@ -140,6 +140,10 @@ class chatBox{
this.displayAutocomplete();
}
tokeWith(user){
this.commandPreprocessor.preprocess(user == this.client.user.user ? "!toke up fuckers" : `!toke up ${user}`);
}
send(event){
if((!event || !event.key || event.key == "Enter") && this.chatPrompt.value){
this.commandPreprocessor.preprocess(this.chatPrompt.value);

View file

@ -101,7 +101,6 @@ class userList{
var highLevel = document.createElement('p');
highLevel.classList.add("user-list-high-level","high-level");
highLevel.innerHTML = `${user.highLevel}`;
userSpan.appendChild(highLevel);
//Create nameplate
var userEntry = document.createElement('p');
@ -115,10 +114,30 @@ class userList{
//Add classes to classList
userEntry.classList.add("chat-panel-users","user-entry",flair);
//Add high-level username to nameplate
userSpan.appendChild(highLevel);
userSpan.appendChild(userEntry);
//Setup profile tooltip
userSpan.addEventListener('mouseenter',(event)=>{utils.ux.displayTooltip(event, `profile?user=${user.user}`, true, null, true);});
//Setup profile context menu
userSpan.addEventListener('click', renderContextMenu.bind(this));
userSpan.addEventListener('contextmenu', renderContextMenu.bind(this));
this.userList.appendChild(userSpan);
function renderContextMenu(event){
//Setup menu map
let menuMap = new Map([
["Profile", ()=>{this.client.cPanel.setActivePanel(new panelObj(client, `${user.user}`, `/panel/profile?user=${user.user}`))}],
["Mention", ()=>{client.chatBox.catChat(`${user.user} `)}],
["Toke With", ()=>{client.chatBox.tokeWith(user.user)}],
]);
//Display the menu
utils.ux.displayContextMenu(event, user.user, menuMap);
}
}
toggleUI(show = !this.userDiv.checkVisibility()){

View file

@ -34,13 +34,51 @@ class canopyUXUtils{
new canopyUXUtils.popup(`<h3>Server Error:</h3><p><br>${err.msg}</p>`);
});
}catch(err){
console.error("Display Error Error:");
console.error("Display Error Body:");
console.error(body);
console.error("Display Error Error:");
console.error(err);
}
}
displayTooltip(event, content, ajaxTooltip, cb, soft = false){
//Create the tooltip
const tooltip = new canopyUXUtils.tooltip(content, ajaxTooltip, ()=>{
//Call mouse move again after ajax load to re-calculate position within context of the new content
tooltip.moveToMouse(event);
//If we have a callback function
if(typeof cb == "function"){
//Call async callback
cb();
}
});
//Move the tooltip with the mouse
event.target.addEventListener('mousemove', tooltip.moveToMouse.bind(tooltip));
//Do intial mouse move
tooltip.moveToMouse(event);
//remove the tooltip on mouseleave
event.target.addEventListener('mouseleave', tooltip.remove.bind(tooltip));
if(soft){
//remove the tooltip on context menu open
event.target.addEventListener('click', tooltip.remove.bind(tooltip));
event.target.addEventListener('contextmenu', tooltip.remove.bind(tooltip));
}
}
displayContextMenu(event, title, menuMap){
event.preventDefault();
//Create context menu
const contextMenu = new canopyUXUtils.contextMenu(title, menuMap);
//Move context menu to mouse
contextMenu.moveToMouse(event);
}
//We should really move this over to uxutils along with newrow & newtable functions
newTableCell(content, firstCol = false){
@ -174,6 +212,50 @@ class canopyUXUtils{
}
}
static contextMenu = class extends this.tooltip{
constructor(title, menuMap){
//Call inherited tooltip constructor
super('Loading Menu...');
//Set tooltip class
this.tooltip.classList.add('context-menu');
//Set title and menu map
this.title = title;
this.menuMap = menuMap;
this.constructMenu();
}
constructMenu(){
//Clear out tooltip
this.tooltip.innerHTML = '';
//Create menu title
const menuTitle = document.createElement('h2');
menuTitle.innerHTML = this.title;
//Append the title to the tooltip
this.tooltip.append(menuTitle);
for(let choice of this.menuMap){
//Create button
const button = document.createElement('button');
button.innerHTML = choice[0];
//Add event listeners
button.addEventListener('click', choice[1]);
//Append the button to the menu div
this.tooltip.appendChild(button);
}
//Create event listener to remove tooltip whenever anything is clicked, inside or out of the menu
//Little hacky but we have to do it a bit later to prevent the opening event from closing the menu
setTimeout(()=>{document.body.addEventListener('click', this.remove.bind(this));}, 1);
setTimeout(()=>{document.body.addEventListener('contextmenu', this.remove.bind(this));}, 1);
}
}
static popup = class{
constructor(content, ajaxPopup = false, cb){
//Define non-popup node values
@ -326,7 +408,6 @@ class canopyUXUtils{
}
drag(event){
if(this.dragLock){
if(this.leftHandle){
//get difference between mouse and right edge of element