modify guild full-window overlay

This commit is contained in:
Michael Peters 2021-12-05 19:42:14 -06:00
parent d214059227
commit bbee168041
10 changed files with 744 additions and 497 deletions

View File

@ -78,8 +78,6 @@ export default function createGuildTitleContextMenu(document: Document, q: Q, ui
} else {
const overlay = createGuildSettingsOverlay(document, q, guild, guildMeta);
document.body.appendChild(overlay);
q.$$$(overlay, '.text-input').focus();
ElementsUtil.setCursorToEnd(q.$$$(overlay, '.text-input'));
}
});
}

View File

@ -21,8 +21,8 @@ export default function createCreateChannelOverlay(document: Document, q: Q, gui
{ class: 'channel-flavor-divider' },
{ class: 'channel-flavor-text', content: '' }
] },
{ class: 'text-input channel-name', placeholder: 'channel-name', content: '', contenteditable: 'plaintext-only' },
{ class: 'text-input channel-flavor-text', placeholder: 'Flavor Text (optional)', content: '' || '', contenteditable: 'plaintext-only' },
{ class: 'text-input channel-name', 'data-placeholder': 'channel-name', content: '', contenteditable: 'plaintext-only' },
{ class: 'text-input channel-flavor-text', 'data-placeholder': 'Flavor Text (optional)', content: '' || '', contenteditable: 'plaintext-only' },
{ class: 'lower', content: [
{ class: 'error' },
{ class: 'buttons', content: [

View File

@ -15,97 +15,159 @@ import CombinedGuild from '../guild-combined';
export default function createGuildSettingsOverlay(document: Document, q: Q, guild: CombinedGuild, guildMeta: GuildMetadata): HTMLElement {
const element = BaseElements.createOverlay(document, {
class: 'content submit-dialog guild-settings', content: [
{ class: 'guild preview', content: [
{ class: 'icon', content: { tag: 'img', src: './img/loading.svg', alt: 'icon' } },
{ class: 'name', content: guildMeta.name }
class: 'content display-swapper guild-settings', content: [
{ class: 'options', content: [
{ class: 'title', content: guildMeta.name }, // TODO: update on change
{ class: 'choosable chosen', content: 'Overview' },
{ class: 'choosable', content: 'Channels' },
{ class: 'choosable', content: 'Roles' },
{ class: 'choosable', content: 'Invites' },
] },
{ class: 'text-input guild-name', placeholder: 'New Guild Name',
contenteditable: 'plaintext-only', content: guildMeta.name },
{ class: 'image-input guild-icon', content: [
{ tag: 'label', class: 'image-input-label button', content: [
'Select New Icon',
{ class: 'image-input-upload', tag: 'input', type: 'file', accept: '.png,.jpg,.jpeg', style: 'display: none;' }
] }
] },
{ class: 'lower', content: [
{ class: 'error' },
{ class: 'buttons', content: [
{ class: 'button submit', content: 'Save Changes' }
] }
{ class: 'display', content: [
{ class: 'scroll', content: [
{ class: 'metadata', content: [
{ tag: 'label', class: 'image-input-label', content: [
{ class: 'image-input', content: [
{ class: 'icon', content: [
{ tag: 'img', class: 'guild-icon', src: './img/loading.svg', alt: 'icon' },
{ class: 'modify', content: [
{ tag: 'img', src: './img/pencil-icon.png' }
] }
] },
] },
{ tag: 'input', class: 'image-input-upload', type: 'file', accept: '.png,.jpg,.jpeg', style: 'display: none' }
] },
{ class: 'name', content: [
{ class: 'label', content: 'Guild Name' },
{ tag: 'input', type: 'text', class: 'guild-name', 'placeholder': guildMeta.name,
contenteditable: 'plaintext-only', content: guildMeta.name },
] },
] }
] },
{ class: 'popup changes', content: [
{ class: 'content', content: [
{ class: 'tip', content: 'You have unsaved changes' },
{ class: 'actions', content: [
{ class: 'button perdu reset', content: 'Reset' },
{ class: 'button positive save-changes', content: 'Save Changes' }
] }
]}
] },
{ class: 'popup error', content: [
{ class: 'content', content: [
{ class: 'tip' },
{ class: 'actions', content: [
{ class: 'button perdu close', content: 'X' },
] }
] }
] },
] }
]
});
(async () => {
(q.$$$(element, '.icon img') as HTMLImageElement).src = await ElementsUtil.getImageBufferFromResourceFailSoftly(guild, guildMeta.iconResourceId);
})();
let newIconBuff: Buffer | null = null;
let oldName = guildMeta.name;
let newName = guildMeta.name;
async function updatePopups(errMsg: string | null = null) {
if (errMsg) {
q.$$$(element, '.popup.error .tip').innerText = errMsg;
q.$$$(element, '.popup.error').classList.add('enabled');
q.$$$(element, '.popup.changes').classList.remove('enabled');
await ElementsUtil.shakeElement(q.$$$(element, '.popup.error'), 400);
} else if (newIconBuff !== null || newName !== oldName) {
q.$$$(element, '.popup.changes .tip').innerText = 'You have unsaved changes'; // in case it had an error before
q.$$$(element, '.popup.changes .button.save-changes').innerText = 'Save Changes'; // in case it had try again before
q.$$$(element, '.popup.error').classList.remove('enabled');
q.$$$(element, '.popup.changes').classList.add('enabled');
} else {
q.$$$(element, '.popup.error').classList.remove('enabled');
q.$$$(element, '.popup.changes').classList.remove('enabled');
}
}
function reset() {
q.$$$<HTMLInputElement>(element, 'input.guild-name').value = oldName;
(async () => {
q.$$$<HTMLImageElement>(element, '.icon img').src = await ElementsUtil.getImageBufferFromResourceFailSoftly(guild, guildMeta.iconResourceId);
})();
newIconBuff = null;
newName = oldName;
}
reset(); // sets icon
q.$$$(element, '.popup.error .button.close').addEventListener('click', () => {
updatePopups()
});
q.$$$(element, '.popup.changes .button.reset').addEventListener('click', () => {
reset();
updatePopups();
});
BaseElements.bindImageUploadEvents(q.$$$(element, '.image-input-upload') as HTMLInputElement, {
maxSize: Globals.MAX_ICON_SIZE,
acceptedMimeTypes: [ 'image/png', 'image/jpeg', 'image/jpg' ],
onChangeStart: () => { q.$$$(element, '.error').innerText = ''; },
onChangeStart: async () => await updatePopups(),
onCleared: () => {},
onError: async (errMsg) => {
q.$$$(element, '.error').innerText = errMsg;
await ElementsUtil.shakeElement(q.$$$(element, '.image-input-upload.guild-icon'), 400);
},
onLoaded: (buff, src) => {
onError: async (errMsg) => await updatePopups(errMsg),
onLoaded: async (buff, src) => {
newIconBuff = buff;
(q.$$$(element, '.guild .icon img') as HTMLImageElement).src = src;
(q.$$$(element, 'img.guild-icon') as HTMLImageElement).src = src;
await updatePopups();
}
});
q.$$$(element, '.text-input').addEventListener('keydown', (e) => {
if (e.key == 'Enter') {
q.$$$(element, 'input.guild-name').addEventListener('input', (e) => {
newName = q.$$$<HTMLInputElement>(element, 'input.guild-name').value;
updatePopups();
});
q.$$$(element, 'input.guild-name').addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
q.$$$(element, '.button.submit').click();
q.$$$(element, '.button.save-changes').click();
}
});
q.$$$(element, '.text-input').addEventListener('input', () => {
q.$$$(element, '.guild.preview .name').innerText = q.$$$(element, '.text-input').innerText;
});
let submitting = false;
q.$$$(element, '.button.submit').addEventListener('click', async () => {
q.$$$(element, '.button.save-changes').addEventListener('click', async () => {
if (submitting) return;
submitting = true;
q.$$$(element, '.error').innerText = '';
q.$$$(element, '.button.submit').innerText = 'Saving...';
q.$$$(element, '.button.save-changes').innerText = 'Saving...';
const newName = q.$$$(element, '.text-input').innerText;
if (newName == guildMeta.name && newIconBuff == null) {
// nothing changed, close the dialog
element.removeSelf();
if (newName == oldName && newIconBuff == null) {
// nothing changed
updatePopups();
submitting = false;
return;
}
let success = false;
if (newName != guildMeta.name && newName.length == 0) {
if (newName != oldName && newName.length == 0) {
LOG.warn('attempted to set empty guild name');
q.$$$(element, '.button.submit').innerText = 'Try Again';
q.$$$(element, '.error').innerText = 'New name is empty';
await ElementsUtil.shakeElement(q.$$$(element, '.button.submit'), 400);
} else if (newName != guildMeta.name && newName.length > Globals.MAX_GUILD_NAME_LENGTH) {
q.$$$(element, '.button.save-changes').innerText = 'Try Again';
q.$$$(element, '.popup.changes .tip').innerText = 'New name is empty';
await ElementsUtil.shakeElement(q.$$$(element, '.button.save-changes'), 400);
} else if (newName != oldName && newName.length > Globals.MAX_GUILD_NAME_LENGTH) {
LOG.warn('attempted to oversized guild name');
q.$$$(element, '.button.submit').innerText = 'Try Again';
q.$$$(element, '.error').innerText = 'New name is too long. ' + newName.length + ' > ' + Globals.MAX_GUILD_NAME_LENGTH;
await ElementsUtil.shakeElement(q.$$$(element, '.button.submit'), 400);
q.$$$(element, '.button.save-changes').innerText = 'Try Again';
q.$$$(element, '.popup.changes .tip').innerText = 'New name is too long. ' + newName.length + ' > ' + Globals.MAX_GUILD_NAME_LENGTH;
await ElementsUtil.shakeElement(q.$$$(element, '.button.save-changes'), 400);
} else { // client-size icon size checks are handled above
let failed = false;
// Set Name
if (newName != guildMeta.name) {
if (newName != oldName) {
try {
await guild.requestSetGuildName(newName);
guildMeta = await guild.fetchMetadata();
} catch (e) {
LOG.error('error setting new guild name', e);
q.$$$(element, '.button.submit').innerText = 'Try Again';
q.$$$(element, '.error').innerText = 'Error setting new guild name';
await ElementsUtil.shakeElement(q.$$$(element, '.button.submit'), 400);
q.$$$(element, '.button.save-changes').innerText = 'Try Again';
q.$$$(element, '.popup.changes .tip').innerText = 'Error setting new guild name';
await ElementsUtil.shakeElement(q.$$$(element, '.button.save-changes'), 400);
failed = true;
}
}
@ -118,7 +180,7 @@ export default function createGuildSettingsOverlay(document: Document, q: Q, gui
} catch (e) {
LOG.error('error setting new guild icon', e);
q.$$$(element, '.button.submit').innerText = 'Try Again';
q.$$$(element, '.error').innerText = 'Error setting new guild icon';
q.$$$(element, '.popup.changes .tip').innerText = 'Error setting new guild icon';
await ElementsUtil.shakeElement(q.$$$(element, '.button.submit'), 400);
failed = true;
}
@ -127,11 +189,12 @@ export default function createGuildSettingsOverlay(document: Document, q: Q, gui
success = !failed;
}
q.$$$(element, '.text-input').setAttribute('contenteditable', 'plaintext-only');
q.$$$(element, '.text-input').focus();
if (success) {
element.removeSelf();
q.$$$(element, '.options .title').innerText = newName;
q.$$$(element, '.guild-name').setAttribute('placeholder', newName);
oldName = newName;
newIconBuff = null;
updatePopups();
}
submitting = false;

View File

@ -21,8 +21,8 @@ export default function createModifyChannelOverlay(document: Document, q: Q, gui
{ class: 'channel-flavor-divider' },
{ class: 'channel-flavor-text', content: channel.flavorText || '' }
] },
{ class: 'text-input channel-name', placeholder: 'channel-name', content: channel.name, contenteditable: 'plaintext-only' },
{ class: 'text-input channel-flavor-text', placeholder: 'Flavor Text (optional)', content: channel.flavorText || '', contenteditable: 'plaintext-only' },
{ class: 'text-input channel-name', 'data-placeholder': 'channel-name', content: channel.name, contenteditable: 'plaintext-only' },
{ class: 'text-input channel-flavor-text', 'data-placeholder': 'Flavor Text (optional)', content: channel.flavorText || '', contenteditable: 'plaintext-only' },
{ class: 'lower', content: [
{ class: 'error' },
{ class: 'buttons', content: [

View File

@ -353,7 +353,7 @@ export default class BaseElements {
const buff = Buffer.from(await file.arrayBuffer());
const typeResult = await FileType.fromBuffer(buff);
if (!typeResult || !acceptedMimeTypes.includes(typeResult.mime)) {
await onError('Invalid Image Type. Accepted Types: ' + acceptedMimeTypes.join(', '));
await onError('Invalid Image Type. Accepted Types: ' + acceptedMimeTypes.map(type => type.replace('image/', '')).join(', '));
return;
}
let src: string | null = null;

View File

@ -15,24 +15,24 @@ export default class Q {
this.document = document;
}
public $(queryString: string): HTMLElement {
public $<T = HTMLElement>(queryString: string): T {
const element = this.document.querySelector<HTMLElement>(queryString);
if (element === null) {
throw new SelectorError(`unable to find [${queryString}] in document`);
}
return element;
return element as unknown as T; // dangerous but convenient
}
public $$(queryString: string): HTMLElement[] {
return Array.from(this.document.querySelectorAll<HTMLElement>(queryString));
}
public $$$(baseElement: HTMLElement, queryString: string): HTMLElement {
public $$$<T = HTMLElement>(baseElement: HTMLElement, queryString: string): T {
const element = baseElement.querySelector<HTMLElement>(queryString);
if (element === null) {
throw new SelectorError(`unable to find [${queryString}] in document`);
}
return element;
return element as unknown as T; // dangerous but convenient
}
public $$$$(baseElement: HTMLElement, queryString: string): HTMLElement[] {

View File

@ -11,4 +11,28 @@
&:hover {
background-color: $brand-hover;
}
&.positive {
background-color: $background-button-positive;
&:hover {
background-color: $background-button-positive-hover;
}
}
&.negative {
background-color: $background-button-negative;
&:negative {
background-color: $background-button-negative-hover;
}
}
&.perdu {
background-color: $background-button-perdu;
&:hover {
background-color: $background-button-perdu-hover;
}
}
}

View File

@ -32,4 +32,5 @@ body {
[contenteditable=plaintext-only]:empty::before{
content:attr(data-placeholder);
cursor: text;
color: $text-muted;
}

View File

@ -3,429 +3,571 @@
/* Popup Image Overlay */
body > .overlay {
/* Note: skip top 22px so we don't overlay on the title bar */
position: absolute;
width: 100vw;
height: calc(100vh - 22px);
top: 22px;
left: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: $background-overlay;
/* Popup Image */
.content.popup-image {
display: flex;
flex-flow: column;
align-items: center;
> img {
max-width: 70vw;
max-height: 70vh;
}
.download {
background-color: $background-primary;
border-radius: 8px;
padding: 8px;
display: flex;
align-items: center;
margin-top: 8px;
justify-content: space-between;
}
.download .info {
margin-right: 8px;
}
.download .info .name {
font-weight: 600;
color: $text-normal;
}
.download .info .size {
font-size: 12px;
font-weight: 500;
color: $text-sending;
}
}
/* Drop Target Overlay */
.content.drop-target {
box-sizing: border-box;
width: calc(100vw - 32px);
height: calc(100vh - 22px - 32px);
margin: 16px;
border: 4px dashed $background-primary;
border-radius: 16px;
display: flex;
justify-content: center;
align-items: center;
* {
pointer-events: none;
}
.message {
border-radius: 8px;
padding: 32px;
background-color: $background-primary;
color: $header-primary;
font-size: 48px;
font-weight: 600;
}
}
/* Upload Overlay */
.content.upload {
background-color: $background-primary;
width: 500px;
border-radius: 12px;
padding: 16px;
.title {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
}
.title img {
max-width: 128px;
max-height: 128px;
border-radius: 8px;
margin-right: 16px;
}
.title .right > *:not(:last-child) {
margin-bottom: 8px;
}
.title .right > * {
color: $text-normal;
}
.title .right > .name {
font-weight: 600;
font-size: 1.25em;
color: $header-primary;
}
.text-input {
color: $text-normal;
background-color: $channeltextarea-background;
border-radius: 8px;
max-height: 100px;
overflow-y: scroll;
padding: 14px 16px 14px 16px;
}
.text-input:focus {
outline: none;
}
.lower {
margin-top: 16px;
display: flex;
align-items: center;
}
.lower .error {
flex: 1;
color: $header-primary;
}
.buttons {
display: flex;
justify-content: flex-end;
}
}
/* General Submit Dialog Overlays */
.content.submit-dialog {
background-color: $background-secondary;
border-radius: 8px;
.image-input {
margin: 16px;
display: flex;
align-items: center;
}
.text-input {
margin: 16px;
color: $text-normal;
background-color: $channeltextarea-background;
border-radius: 8px;
max-height: 100px;
overflow-y: scroll;
padding: 14px 16px;
}
.text-input:focus {
outline: none;
}
.lower {
padding: 16px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: $background-tertiary;
}
.error {
color: $text-normal;
}
.buttons {
margin-left: 16px;
display: flex;
}
}
/* Personalization Overlay */
.content.personalize {
min-width: 350px;
.message {
margin: 16px;
padding: 0 4px;
}
}
/* guild Settings Overlay */
.content.guild-settings {
min-width: 350px;
.preview {
display: flex;
align-items: center;
margin: 16px;
}
.preview .icon img {
width: 48px;
height: 48px;
border-radius: 24px;
}
.preview .icon {
margin-right: 16px;
}
.preview .name {
color: $header-primary;
font-weight: 500;
}
}
/* Add guild Overlay */
.content.add-guild {
min-width: 350px;
background-color: $background-secondary;
border-radius: 12px;
.divider {
margin: 16px;
height: 1px;
background-color: $background-primary;
}
.preview {
margin: 16px;
display: flex;
align-items: center;
}
.preview .icon {
width: 64px;
height: 64px;
margin-right: 16px;
border-radius: 16px;
}
.preview .name {
color: $header-primary;
font-size: 1.25em;
font-weight: 600;
}
.preview .url {
color: $text-normal;
}
.preview .expires {
color: $text-muted;
font-size: 12px;
font-weight: 600;
}
.message {
margin: 16px;
padding: 0;
}
.avatar-input {
margin: 16px;
display: flex;
align-items: center;
}
.display-name-input {
margin: 16px;
color: $text-normal;
background-color: $channeltextarea-background;
border-radius: 8px;
max-height: 100px;
overflow-y: scroll;
padding: 14px 16px;
}
.display-name-input:focus {
outline: none;
}
.lower {
padding: 16px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: $background-tertiary;
}
.error {
color: $text-normal;
}
.error::first-letter {
text-transform: uppercase;
}
.buttons {
margin-left: 16px;
display: flex;
}
}
/* Modify Channel Overlay */
.content.modify-channel {
min-width: 350px;
max-width: calc(100vw - 80px);
.preview.channel-title {
border-top-left-radius: 12px;
border-top-right-radius: 12px;
padding-right: 8px;
}
.text-input.channel-name {
text-transform: lowercase;
}
}
/* Error Message Overlay */
.content.error-message {
background-color: $background-secondary;
padding: 16px;
border-radius: 12px;
.title {
color: $header-primary;
font-size: 1.25em;
font-weight: 600;
line-height: 1;
margin-bottom: 12px;
}
.message {
padding: 0;
color: $text-normal;
}
}
/* View Tokens Overlay */
.content.token-log {
background-color: $background-secondary;
padding: 16px;
border-radius: 12px;
color: $text-normal;
.tokens .token-display {
border-radius: 8px;
padding: 8px;
display: flex;
align-items: center;
}
.tokens .token-display:hover {
background-color: $background-secondary-alt;
}
.tokens .token-display:last-child {
margin-bottom: 0;
}
.tokens .instance {
display: flex;
justify-content: space-between;
width: 400px;
margin-right: 8px;
}
.tokens .instance .left {
text-align: left;
}
.tokens .instance .right {
text-align: right;
}
.tokens .instance .member {
font-weight: 600;
font-size: 16px;
}
.tokens .instance .token {
font-size: 12px;
color: $text-muted;
}
.tokens .instance .expires,
.tokens .instance .created {
font-size: 14px;
}
.tokens .buttons {
display: flex;
}
.tokens .buttons .download {
cursor: pointer;
border-radius: 4px;
padding: 8px;
background-color: $away;
margin-right: 8px;
}
.tokens .buttons .revoke {
cursor: pointer;
border-radius: 4px;
padding: 8px;
background-color: $busy
}
}
/* Note: skip top 22px so we don't overlay on the title bar */
position: absolute;
width: 100vw;
height: calc(100vh - 22px);
top: 22px;
left: 0;
display: flex;
justify-content: center;
align-items: center;
background-color: $background-overlay;
/* General Controls */
input[type=text], .text-input {
font-size: inherit;
font-family: inherit;
color: $text-normal;
background-color: $background-input;
border: 1px solid $border-input;
border-radius: 3px;
max-height: 100px;
overflow-y: scroll;
padding: 8px;
&:hover {
border-color: $border-input-hover;
}
&:focus {
outline: none;
border-color: $border-input-focus;
}
}
.label {
font-size: 0.75em;
font-weight: bold;
color: $interactive-normal;
text-transform: uppercase;
margin-bottom: 2px;
}
/* Popup Image */
> .content.popup-image {
display: flex;
flex-flow: column;
align-items: center;
> img {
max-width: 70vw;
max-height: 70vh;
}
.download {
background-color: $background-primary;
border-radius: 8px;
padding: 8px;
display: flex;
align-items: center;
margin-top: 8px;
justify-content: space-between;
}
.download .info {
margin-right: 8px;
}
.download .info .name {
font-weight: 600;
color: $text-normal;
}
.download .info .size {
font-size: 12px;
font-weight: 500;
color: $text-sending;
}
}
/* Drop Target Overlay */
> .content.drop-target {
box-sizing: border-box;
width: calc(100vw - 32px);
height: calc(100vh - 22px - 32px);
margin: 16px;
border: 4px dashed $background-primary;
border-radius: 16px;
display: flex;
justify-content: center;
align-items: center;
* {
pointer-events: none;
}
.message {
border-radius: 8px;
padding: 32px;
background-color: $background-primary;
color: $header-primary;
font-size: 48px;
font-weight: 600;
}
}
/* Upload Overlay */
> .content.upload {
background-color: $background-primary;
width: 500px;
border-radius: 12px;
padding: 16px;
.title {
display: flex;
align-items: flex-start;
margin-bottom: 16px;
}
.title img {
max-width: 128px;
max-height: 128px;
border-radius: 8px;
margin-right: 16px;
}
.title .right > *:not(:last-child) {
margin-bottom: 8px;
}
.title .right > * {
color: $text-normal;
}
.title .right > .name {
font-weight: 600;
font-size: 1.25em;
color: $header-primary;
}
.lower {
margin-top: 16px;
display: flex;
align-items: center;
}
.lower .error {
flex: 1;
color: $header-primary;
}
.buttons {
display: flex;
justify-content: flex-end;
}
}
/* General Submit Dialog Overlays */
> .content.submit-dialog {
background-color: $background-secondary;
border-radius: 8px;
.image-input {
margin: 16px;
display: flex;
align-items: center;
}
.text-input {
margin: 16px;
color: $text-normal;
background-color: $channeltextarea-background;
border-radius: 8px;
max-height: 100px;
overflow-y: scroll;
padding: 14px 16px;
}
.text-input:focus {
outline: none;
}
.lower {
padding: 16px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: $background-tertiary;
}
.error {
color: $text-normal;
}
.buttons {
margin-left: 16px;
display: flex;
}
}
/* Personalization Overlay */
> .content.personalize {
min-width: 350px;
.message {
margin: 16px;
padding: 0 4px;
}
}
/* Add guild Overlay */
> .content.add-guild {
min-width: 350px;
background-color: $background-secondary;
border-radius: 12px;
.divider {
margin: 16px;
height: 1px;
background-color: $background-primary;
}
.preview {
margin: 16px;
display: flex;
align-items: center;
}
.preview .icon {
width: 64px;
height: 64px;
margin-right: 16px;
border-radius: 16px;
}
.preview .name {
color: $header-primary;
font-size: 1.25em;
font-weight: 600;
}
.preview .url {
color: $text-normal;
}
.preview .expires {
color: $text-muted;
font-size: 12px;
font-weight: 600;
}
.message {
margin: 16px;
padding: 0;
}
.avatar-input {
margin: 16px;
display: flex;
align-items: center;
}
.display-name-input {
margin: 16px;
color: $text-normal;
background-color: $channeltextarea-background;
border-radius: 8px;
max-height: 100px;
overflow-y: scroll;
padding: 14px 16px;
}
.display-name-input:focus {
outline: none;
}
.lower {
padding: 16px;
border-bottom-left-radius: 8px;
border-bottom-right-radius: 8px;
display: flex;
justify-content: space-between;
align-items: center;
background-color: $background-tertiary;
}
.error {
color: $text-normal;
}
.error::first-letter {
text-transform: uppercase;
}
.buttons {
margin-left: 16px;
display: flex;
}
}
/* Modify Channel Overlay */
> .content.modify-channel {
min-width: 350px;
max-width: calc(100vw - 80px);
.preview.channel-title {
border-top-left-radius: 12px;
border-top-right-radius: 12px;
padding-right: 8px;
}
.text-input.channel-name {
text-transform: lowercase;
}
}
/* Offers selectors on left side and content on the right side */
> .content.display-swapper {
height: calc(100vh - 50px - 22px); // fill height
width: 940px - 100px;
display: flex;
$content-border-radius: 4px;
> .options {
box-sizing: border-box;
background-color: $background-secondary;
width: 226px;
padding: 32px 16px;
border-top-left-radius: $content-border-radius;
border-bottom-left-radius: $content-border-radius;
> .title {
font-size: 0.75em;
font-weight: bold;
color: $interactive-normal;
text-transform: uppercase;
padding: 3px 12px;
margin-bottom: 4px;
}
> .choosable {
color: $interactive-normal;
cursor: pointer;
padding: 6px 12px;
border-radius: 3px;
margin-bottom: 4px;
&.chosen {
color: $interactive-active;
background-color: $background-modifier-selected
}
&:not(.chosen):hover {
color: $interactive-hover;
background-color: $background-modifier-hover;
}
}
}
> .display {
flex: 1;
border-top-right-radius: $content-border-radius;
border-bottom-right-radius: $content-border-radius;
background-color: $background-primary;
position: relative;
> .scroll {
margin: 32px;
}
> .popup {
position: absolute;
bottom: 0;
left: 0;
box-sizing: border-box;
width: 100%;
&:not(.enabled) {
display: none;
}
.content {
margin: 16px;
padding: 12px 12px 12px 16px;
display: flex;
justify-content: space-between;
align-items: center;
color: $interactive-active;
background-color: $background-popup-message;
border-radius: 8px;
}
.tip {
font-weight: 500;
}
.actions {
display: flex;
.button {
padding: 8px 12px;
font-size: 0.85em;
font-weight: bold;
}
:not(:last-child) {
margin-right: 4px;
}
}
}
}
}
/* guild Settings Overlay */
> .content.display-swapper.guild-settings {
min-width: 350px;
.metadata {
display: flex;
margin-bottom: 12px;
.image-input-label {
cursor: pointer;
}
.icon {
position: relative;
> img {
width: 64px;
height: 64px;
border-radius: 8px;
}
.modify {
position: absolute;
background-color: $brand;
padding: 6px;
border-radius: 16px;
right: -4px;
bottom: -4px;
> img {
display: block;
width: 10px;
height: 10px;
}
}
}
.name {
margin-left: 16px;
}
.guild-name.text-input {
color: $header-primary;
font-weight: 500;
width: 150px;
}
}
.button.metadata-submit {
display: inline-block;
}
}
/* Error Message Overlay */
> .content.error-message {
background-color: $background-secondary;
padding: 16px;
border-radius: 12px;
.title {
color: $header-primary;
font-size: 1.25em;
font-weight: 600;
line-height: 1;
margin-bottom: 12px;
}
.message {
padding: 0;
color: $text-normal;
}
}
/* View Tokens Overlay */
> .content.token-log {
background-color: $background-secondary;
padding: 16px;
border-radius: 12px;
color: $text-normal;
.tokens .token-display {
border-radius: 8px;
padding: 8px;
display: flex;
align-items: center;
}
.tokens .token-display:hover {
background-color: $background-secondary-alt;
}
.tokens .token-display:last-child {
margin-bottom: 0;
}
.tokens .instance {
display: flex;
justify-content: space-between;
width: 400px;
margin-right: 8px;
}
.tokens .instance .left {
text-align: left;
}
.tokens .instance .right {
text-align: right;
}
.tokens .instance .member {
font-weight: 600;
font-size: 16px;
}
.tokens .instance .token {
font-size: 12px;
color: $text-muted;
}
.tokens .instance .expires,
.tokens .instance .created {
font-size: 14px;
}
.tokens .buttons {
display: flex;
}
.tokens .buttons .download {
cursor: pointer;
border-radius: 4px;
padding: 8px;
background-color: $away;
margin-right: 8px;
}
.tokens .buttons .revoke {
cursor: pointer;
border-radius: 4px;
padding: 8px;
background-color: $busy
}
}
}

View File

@ -2,14 +2,17 @@
$header-primary: #ffffff;
$header-secondary: #b9bbbe;
$text-normal: #dcddde;
$text-sending: #9ca1a8;
$text-muted: #72767d;
$text-link: #00b0f4;
$interactive-normal: #b9bbbe;
$interactive-hover: #dcddde;
$interactive-active: #ffffff;
$interactive-muted: #4f545c;
$background-primary: #36393f;
$background-secondary: #2f3136;
$background-secondary-alt: #292b2f;
@ -19,9 +22,18 @@ $background-modifier-hover: rgba(79, 84, 92, 0.16);
$background-modifier-selected: rgba(79, 84, 92, 0.32);
$background-modifier-accent: hsla(0, 0%, 100%, 0.06);
$background-message-hover: rgba(4, 4, 5, 0.07);
$background-overlay: rgba(0, 0, 0, 0.85);
$background-overlay: rgba(12, 13, 14, 0.75);
$background-popup-message: rgba(30, 31, 34, 0.75);
$background-input: #2f3136;
$border-input: #1d1e22;
$border-input-hover: #0b0c0e;
$border-input-focus: #0099ff;
$channels-default: #8e9297;
$channeltextarea-background: #40444b;
$brand: #7289ba; /* yea that's a direct copy from discord */
$brand-hover: #677bc4; /* thicc */
$error: #d71f28; /* In fact, not copied! */
@ -32,3 +44,10 @@ $away: #dab01d;
$busy: #ce1515;
$item-red: #da4040;
$background-button-positive: #52a13e;
$background-button-positive-hover: #3f862d;
$background-button-negative: #cc302b;
$background-button-negative-hover: #b82722;
$background-button-perdu: rgba(0, 0, 0, 0);
$background-button-perdu-hover: #1e2024;