modify guild full-window overlay
This commit is contained in:
parent
d214059227
commit
bbee168041
@ -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'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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: [
|
||||
|
@ -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;
|
||||
|
@ -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: [
|
||||
|
@ -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;
|
||||
|
@ -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[] {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +32,5 @@ body {
|
||||
[contenteditable=plaintext-only]:empty::before{
|
||||
content:attr(data-placeholder);
|
||||
cursor: text;
|
||||
color: $text-muted;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user