diff --git a/.prettierignore b/.prettierignore index ce6945c..870a11b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,4 @@ dist *.md -*.html +#*.html *.css \ No newline at end of file diff --git a/src/css/_colors-dark.scss b/src/css/_colors-dark.scss index 9815292..dcb17ca 100644 --- a/src/css/_colors-dark.scss +++ b/src/css/_colors-dark.scss @@ -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; +} \ No newline at end of file diff --git a/src/css/cards.scss b/src/css/cards.scss index fe929ff..2c43087 100644 --- a/src/css/cards.scss +++ b/src/css/cards.scss @@ -1,3 +1,4 @@ +// module for cards @import 'colors-dark'; .cards { diff --git a/src/css/dialog.scss b/src/css/dialog.scss new file mode 100644 index 0000000..ede3974 --- /dev/null +++ b/src/css/dialog.scss @@ -0,0 +1,44 @@ +// for our new 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; +} diff --git a/src/css/elements.scss b/src/css/elements.scss new file mode 100644 index 0000000..16e68bf --- /dev/null +++ b/src/css/elements.scss @@ -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; +} + diff --git a/src/css/main.scss b/src/css/main.scss index f8b8f95..9aed602 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -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 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; -} diff --git a/src/html/index.html b/src/html/index.html index 6dc5fe6..48655c7 100644 --- a/src/html/index.html +++ b/src/html/index.html @@ -1,136 +1,158 @@ - + - - CollabVM - - - - - - - - - - - - - - - - -
-
+ + CollabVM + + + + + + + + + + + + + + + + +
+ - -
    -
-
-
-

- -
- - - - - - -
- - - - - - - -
-
-
-
+ +
+
+
+ +
-
-
-
-
+
+ + +
+
+
-
-
-
-
+ +
+ +
+ +
+
+
+
+ +
- - - +
+
+
+
+ +
+
+
+
+
+
+
+
+ + + + + +
()
+
+
+
+
+ + +
+
+
+ Username + +
+ + +
+ +
+
+
+ + + + diff --git a/src/ts/main.ts b/src/ts/main.ts index 306c8c9..c1e3c54 100644 --- a/src/ts/main.ts +++ b/src/ts/main.ts @@ -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 { // 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 { 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); } diff --git a/src/ts/modals/alert_modal.ts b/src/ts/modals/alert_modal.ts new file mode 100644 index 0000000..baf8e64 --- /dev/null +++ b/src/ts/modals/alert_modal.ts @@ -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(); +} diff --git a/src/ts/modals/change_username_modal.ts b/src/ts/modals/change_username_modal.ts new file mode 100644 index 0000000..43b3e5b --- /dev/null +++ b/src/ts/modals/change_username_modal.ts @@ -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 { + 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(); + }); +}