Port modals to HTML dialog
Places that used alert() now use a custom dialog. Also the change username modal has been remade. Also styles some elements. These aren't final, more just to actually style them. I would like to use jsx or osmething instead of stuffing these in the html, especially since it would allow us to single-source-of-truth i18n (removing our second-source-of-truth in the i18n module that statically replace stuff) in a lot more places. and make other code a lot less iffy. we do NOT need react or some shit if we do that. just nano-jsx, or something even simpler is more than good enough, and should keep us in our "100 kb smaller" track
This commit is contained in:
parent
b5256da3a5
commit
e3f0ec56a2
|
|
@ -1,4 +1,4 @@
|
|||
dist
|
||||
*.md
|
||||
*.html
|
||||
#*.html
|
||||
*.css
|
||||
|
|
@ -1,10 +1,19 @@
|
|||
// TODO: Maybe we *should* use CSS variables, so the user can pick any theme
|
||||
// and maybe put in hooks for that.. but for now this is fine!
|
||||
// (this really should be called _style-dark @ this point..)
|
||||
|
||||
$root-bg-color: rgb(32, 32, 32);
|
||||
$root-fg-color: rgb(180, 180, 180);
|
||||
|
||||
/** Cards **/
|
||||
// Elements
|
||||
$input-bg-color: rgb(51, 51, 51);
|
||||
// Resist the urge to make these radioactive. I made them kinda grungy
|
||||
// on purpose, and it seems to be okay.
|
||||
$button-bg-color: rgb(51, 107, 145);
|
||||
$button-red-bg-color: rgb(159, 51, 51);
|
||||
$button-green-bg-color: rgb(61, 151, 43);
|
||||
|
||||
// Cards
|
||||
$card-background-color: rgb(48, 48, 48);
|
||||
$card-border-color: rgb(58, 58, 58);
|
||||
$card-box-shadow-color: rgb(0, 0, 0);
|
||||
|
|
@ -13,3 +22,10 @@ $card-image-background-color: #ffffff10;
|
|||
|
||||
$link-color: rgb(100, 140, 200);
|
||||
$link-visited-color: rgb(160, 140, 200);
|
||||
|
||||
// Root mixin
|
||||
.root {
|
||||
background-color: $root-bg-color;
|
||||
color: $root-fg-color;
|
||||
font-family: 'Segoe UI', Tahoma, Cantarell, sans-serif;
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
// module for cards
|
||||
@import 'colors-dark';
|
||||
|
||||
.cards {
|
||||
|
|
|
|||
44
src/css/dialog.scss
Normal file
44
src/css/dialog.scss
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// for our new <dialog> based modals
|
||||
@import 'colors-dark';
|
||||
|
||||
/* a bit of a "lazy" curve. should probably be in a shared file */
|
||||
@keyframes dialogFadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
35% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// don't really know how i can animate "out",
|
||||
// at least without hacks, but oh well
|
||||
//
|
||||
// this will also probably be snappier than the mess that is
|
||||
// the bootstrap modals..
|
||||
|
||||
dialog::backdrop {
|
||||
animation: dialogFadeIn 0.3s;
|
||||
background-color: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
dialog {
|
||||
@extend .root;
|
||||
|
||||
color: inherit;
|
||||
background-color: $root-fg-color $card-background-color;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 0px 8px 0px $root-fg-color $card-box-shadow-color;
|
||||
}
|
||||
|
||||
:modal {
|
||||
animation: dialogFadeIn 0.2s;
|
||||
}
|
||||
|
||||
.dialog-alignright {
|
||||
float: right;
|
||||
}
|
||||
58
src/css/elements.scss
Normal file
58
src/css/elements.scss
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
// Module for element styling
|
||||
// THIS IS NOT DONE!
|
||||
@import 'colors-dark';
|
||||
|
||||
.button-default {
|
||||
@extend .root;
|
||||
|
||||
background-color: $button-bg-color !important;
|
||||
border-radius: 6px;
|
||||
border-width: 0;
|
||||
color: #ffffff !important;
|
||||
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
padding: 10px 12px;
|
||||
|
||||
text-align: center;
|
||||
transition: all 200ms;
|
||||
vertical-align: baseline;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
button {
|
||||
@extend .button-default;
|
||||
}
|
||||
|
||||
.button-red {
|
||||
background-color: $button-red-bg-color !important;
|
||||
}
|
||||
|
||||
.button-green {
|
||||
background-color: $button-green-bg-color !important;
|
||||
}
|
||||
|
||||
|
||||
input[type='text'] {
|
||||
padding: 0.1em;
|
||||
height: 1.75em;
|
||||
//position: relative;
|
||||
|
||||
outline: none;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
background-color: $input-bg-color;
|
||||
font-size: 16px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
|
|
@ -2,13 +2,14 @@
|
|||
@import 'colors-dark';
|
||||
|
||||
// modules
|
||||
@import 'elements';
|
||||
@import 'cards';
|
||||
|
||||
|
||||
|
||||
html,
|
||||
body {
|
||||
background-color: $root-bg-color;
|
||||
color: $root-fg-color;
|
||||
font-family: 'Segoe UI', Tahoma, Cantarell, sans-serif;
|
||||
@extend .root;
|
||||
margin: unset;
|
||||
}
|
||||
|
||||
|
|
@ -331,32 +332,8 @@ a[data-cvm-node='vm0b0t'] {
|
|||
}
|
||||
}
|
||||
|
||||
// for our new <dialog> based modals
|
||||
// This should probably be put into a more uniform file
|
||||
|
||||
/* a bit of a "lazy" curve */
|
||||
@keyframes dialogFadeIn {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
35% {
|
||||
opacity: 0.75;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
// shitty bootstrap polyfill
|
||||
.d-none {
|
||||
display: none;
|
||||
}
|
||||
|
||||
// don't really know how i can animate "out",
|
||||
// at least without hacks, but oh well
|
||||
//
|
||||
// this will also probably be snappier than the mess that is
|
||||
// the bootstrap modals..
|
||||
|
||||
dialog::backdrop {
|
||||
animation: dialogFadeIn 0.7s;
|
||||
}
|
||||
|
||||
:modal {
|
||||
animation: dialogFadeIn 0.4s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!doctype html>
|
||||
<html prefix="og: https://ogp.me/ns#">
|
||||
<head>
|
||||
<title>CollabVM</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="utf-8" />
|
||||
<link href="../css/main.scss" rel="stylesheet" />
|
||||
<script src="https://kit.fontawesome.com/7add23c1ae.js" crossorigin="anonymous"></script>
|
||||
<link rel="icon" href="../assets/favicon.ico">
|
||||
<link rel="icon" href="../assets/favicon.ico" />
|
||||
<meta name="description" content="A website that lets you take turns controlling online virtual machines with complete strangers!" />
|
||||
<!-- Opengraph shit -->
|
||||
<meta property="og:type" content="website" />
|
||||
|
|
@ -20,6 +20,30 @@
|
|||
<!-- Needed for the new modal API, this is where all the modals mount to
|
||||
I know I could probably create it dynamically but I just want it to work OK-->
|
||||
<div id="modalRoot">
|
||||
<!-- TEMP! these will be created in js or jsx -->
|
||||
|
||||
<dialog id="changeUsernameDialog">
|
||||
<form method="dialog">
|
||||
<label>Enter a new username, or leave blank to assign yourself a guest username.</label><br />
|
||||
<section>
|
||||
<input id="usernameInput" type="text" placeholder="A username, or leave blank." />
|
||||
</section>
|
||||
|
||||
<div class="dialog-alignright">
|
||||
<button id="okButton" value="" class="button-green">OK</button>
|
||||
<button id="cancelButton" class="button-red">Cancel</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<dialog id="alertDialog">
|
||||
<form method="dialog">
|
||||
<label id="alertMessage"></label>
|
||||
<div class="dialog-alignright">
|
||||
<button id="okButton" value="" class="button-green">OK</button>
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
</div>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
|
|
@ -55,14 +79,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<ul class="cards" id="vmlist">
|
||||
</ul>
|
||||
<ul class="cards" id="vmlist"></ul>
|
||||
<div class="container-fluid" id="vmview">
|
||||
<div id="vmDisplay"></div>
|
||||
<p id="turnstatus" class="text-light"></p>
|
||||
<div id="voteResetPanel" class="bg-dark text-light" style="display:none;">
|
||||
<div id="voteResetPanel" class="bg-dark text-light" style="display: none">
|
||||
<span id="voteResetHeaderText"></span><br />
|
||||
<button class="btn btn-success" id="voteYesBtn"><i class="fa-solid fa-check"></i> <span id="voteYesBtnText"></span><span class="badge bg-secondary" id="voteYesLabel"></span></button> <button class="btn btn-danger" id="voteNoBtn"><i class="fa-solid fa-ban"></i> <span id="voteNoBtnText"></span><span class="badge bg-secondary" id="voteNoLabel"></span></button><br/>
|
||||
<button class="btn btn-success" id="voteYesBtn"><i class="fa-solid fa-check"></i> <span id="voteYesBtnText"></span><span class="badge bg-secondary" id="voteYesLabel"></span></button>
|
||||
<button class="btn btn-danger" id="voteNoBtn"><i class="fa-solid fa-ban"></i> <span id="voteNoBtnText"></span><span class="badge bg-secondary" id="voteNoLabel"></span></button><br />
|
||||
<span id="voteTimeText"></span>
|
||||
<div id="forceVotePanel">
|
||||
<button class="btn btn-info" id="forceVoteYesBtn"><i class="fa-solid fa-check"></i> <span id="passVoteBtnText"></span></button>
|
||||
|
|
@ -113,9 +137,7 @@
|
|||
<div class="col-md-8">
|
||||
<div class="table-responsive chat-table" id="chatListDiv">
|
||||
<table class="table table-hover table-dark table-borderless">
|
||||
<tbody id="chatList">
|
||||
|
||||
</tbody>
|
||||
<tbody id="chatList"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import VoteStatus from './protocol/VoteStatus.js';
|
|||
import MuteState from './protocol/MuteState.js';
|
||||
import { I18nStringKey, TheI18n } from './i18n.js';
|
||||
import { Format } from './format.js';
|
||||
import { ChangeUsername_Modal } from './modals/change_username_modal.js';
|
||||
import { Alert_Modal } from './modals/alert_modal.js';
|
||||
|
||||
// Elements
|
||||
const w = window as any;
|
||||
|
|
@ -325,13 +327,13 @@ async function openVM(vm: VM): Promise<void> {
|
|||
// TODO: i18n these
|
||||
switch (status) {
|
||||
case 'taken':
|
||||
alert(TheI18n.GetString(I18nStringKey.kError_UsernameTaken));
|
||||
Alert_Modal(TheI18n.GetString(I18nStringKey.kError_UsernameTaken));
|
||||
break;
|
||||
case 'invalid':
|
||||
alert(TheI18n.GetString(I18nStringKey.kError_UsernameInvalid));
|
||||
Alert_Modal(TheI18n.GetString(I18nStringKey.kError_UsernameInvalid));
|
||||
break;
|
||||
case 'blacklisted':
|
||||
alert(TheI18n.GetString(I18nStringKey.kError_UsernameBlacklisted));
|
||||
Alert_Modal(TheI18n.GetString(I18nStringKey.kError_UsernameBlacklisted));
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
|
@ -339,11 +341,11 @@ async function openVM(vm: VM): Promise<void> {
|
|||
VM!.on('turn', (status) => turnUpdate(status));
|
||||
VM!.on('vote', (status: VoteStatus) => voteUpdate(status));
|
||||
VM!.on('voteend', () => voteEnd());
|
||||
VM!.on('votecd', (voteCooldown) => window.alert(TheI18n.GetString(I18nStringKey.kVM_VoteCooldownTimer, voteCooldown)));
|
||||
VM!.on('votecd', (voteCooldown) => window.Alert_Modal(TheI18n.GetString(I18nStringKey.kVM_VoteCooldownTimer, voteCooldown)));
|
||||
VM!.on('login', (rank: Rank, perms: Permissions) => onLogin(rank, perms));
|
||||
|
||||
VM!.on('close', () => {
|
||||
if (!expectedClose) alert(TheI18n.GetString(I18nStringKey.kError_UnexpectedDisconnection));
|
||||
if (!expectedClose) Alert_Modal(TheI18n.GetString(I18nStringKey.kError_UnexpectedDisconnection));
|
||||
closeVM();
|
||||
});
|
||||
|
||||
|
|
@ -415,7 +417,7 @@ async function openHash() {
|
|||
try {
|
||||
if (v !== undefined) await openVM(v);
|
||||
} catch (e) {
|
||||
alert((e as Error).message);
|
||||
Alert_Modal((e as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -631,8 +633,13 @@ elements.sendChatBtn.addEventListener('click', sendChat);
|
|||
elements.chatinput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') sendChat();
|
||||
});
|
||||
elements.changeUsernameBtn.addEventListener('click', () => {
|
||||
let newname = prompt(TheI18n.GetString(I18nStringKey.kVMPrompts_EnterNewUsernamePrompt), w.username);
|
||||
elements.changeUsernameBtn.addEventListener('click', async () => {
|
||||
let newname = await ChangeUsername_Modal(w.username);
|
||||
|
||||
// cancelled
|
||||
if(newname == null)
|
||||
return;
|
||||
|
||||
if (newname === w.username) return;
|
||||
VM?.rename(newname);
|
||||
});
|
||||
|
|
@ -748,7 +755,7 @@ function userModOptions(user: { user: User; element: HTMLTableRowElement }) {
|
|||
if (perms.grabip)
|
||||
addUserDropdownItem(ul, 'Get IP', async () => {
|
||||
let ip = await VM!.getip(user.user.username);
|
||||
alert(ip);
|
||||
Alert_Modal(ip);
|
||||
});
|
||||
tr.appendChild(ul);
|
||||
}
|
||||
|
|
|
|||
9
src/ts/modals/alert_modal.ts
Normal file
9
src/ts/modals/alert_modal.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
const elements = {
|
||||
alertDialog: document.querySelector('#alertDialog') as HTMLDialogElement
|
||||
};
|
||||
|
||||
export function Alert_Modal(text: string) {
|
||||
const alertMessage = elements.alertDialog.querySelector('#alertMessage') as HTMLLabelElement;
|
||||
alertMessage.innerText = text;
|
||||
elements.alertDialog.showModal();
|
||||
}
|
||||
52
src/ts/modals/change_username_modal.ts
Normal file
52
src/ts/modals/change_username_modal.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
const elements = {
|
||||
changeUsernameDialog: document.querySelector('#changeUsernameDialog') as HTMLDialogElement
|
||||
};
|
||||
|
||||
// returns a promise which will resolve with "null" for canceled,
|
||||
// and a username for a username
|
||||
export async function ChangeUsername_Modal(lastUsername: string): Promise<string | null> {
|
||||
return new Promise((res, rej) => {
|
||||
const usernameInput = elements.changeUsernameDialog.querySelector('#usernameInput') as HTMLInputElement;
|
||||
const cancelButton = elements.changeUsernameDialog.querySelector('#cancelButton') as HTMLButtonElement;
|
||||
const okButton = elements.changeUsernameDialog.querySelector('#okButton') as HTMLButtonElement;
|
||||
|
||||
usernameInput.addEventListener('change', (e) => {
|
||||
okButton.value = usernameInput.value;
|
||||
});
|
||||
|
||||
function handleDialog() {
|
||||
resetBox();
|
||||
res(elements.changeUsernameDialog.returnValue);
|
||||
}
|
||||
|
||||
function handleDialogCancel() {
|
||||
resetBox();
|
||||
res(null);
|
||||
}
|
||||
|
||||
function resetBox() {
|
||||
usernameInput.value = '';
|
||||
|
||||
// remove event listener s you google..
|
||||
elements.changeUsernameDialog.removeEventListener('close', handleDialog);
|
||||
elements.changeUsernameDialog.removeEventListener('close', handleDialogCancel);
|
||||
}
|
||||
|
||||
elements.changeUsernameDialog.addEventListener('cancel', (e) => {
|
||||
handleDialogCancel();
|
||||
});
|
||||
|
||||
okButton.addEventListener('click', (ev) => {
|
||||
elements.changeUsernameDialog.addEventListener('close', handleDialog);
|
||||
});
|
||||
|
||||
cancelButton.addEventListener('click', (ev) => {
|
||||
elements.changeUsernameDialog.addEventListener('close', handleDialogCancel);
|
||||
});
|
||||
|
||||
// show the modal!
|
||||
usernameInput.value = lastUsername;
|
||||
okButton.value = lastUsername;
|
||||
elements.changeUsernameDialog.showModal();
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user