jsx is for professionals
This commit is contained in:
parent
6979c450e6
commit
6b17f569bb
@ -19,6 +19,9 @@ import UI from '../ui';
|
||||
import GuildsManager from '../guilds-manager';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
import React from 'react';
|
||||
import ReactHelper from './require/react-helper';
|
||||
|
||||
export interface IAddGuildData {
|
||||
name: string,
|
||||
url: string,
|
||||
@ -58,45 +61,44 @@ export default function createAddGuildOverlay(document: Document, q: Q, ui: UI,
|
||||
|
||||
//LOG.debug('addguilddata:', { addGuildData });
|
||||
|
||||
const element = BaseElements.createOverlay(document, {
|
||||
class: 'content add-guild', content: [
|
||||
{ class: 'preview', content: [
|
||||
{ tag: 'img', class: 'icon', src: addGuildData.iconSrc, alt: 'icon' },
|
||||
{ content: [
|
||||
{ class: 'name', content: addGuildData.name },
|
||||
{ class: 'url', content: addGuildData.url },
|
||||
{ class: 'expires',
|
||||
content: (expired ? 'Invite Expired ' : 'Invite Expires ') + moment(addGuildData.expires).fromNow() }
|
||||
] }
|
||||
] },
|
||||
{ class: 'divider' },
|
||||
{ class: 'message-preview message', content: [
|
||||
{ class: 'member-avatar', content: { tag: 'img', src: './img/loading.svg', alt: 'avatar' } },
|
||||
{ class: 'right', content: [
|
||||
{ class: 'header', content: [
|
||||
{ class: 'member-name', content: displayName },
|
||||
{ class: 'timestamp', content: moment().calendar(ElementsUtil.calendarFormats) }
|
||||
] },
|
||||
{ class: 'content text', content: 'Example Message' }
|
||||
] }
|
||||
] },
|
||||
{ class: 'display-name-input', placeholder: 'Display Name',
|
||||
spellcheck: 'false', contenteditable: 'plaintext-only', content: displayName },
|
||||
{ class: 'avatar-input', content: [
|
||||
{ tag: 'label', class: 'avatar-upload-label button', content: [
|
||||
'Select Avatar',
|
||||
{ class: 'avatar-upload', tag: 'input',
|
||||
type: 'file', accept: '.png,.jpg,.jpeg', style: 'display: none;' },
|
||||
] }
|
||||
] },
|
||||
{ class: 'lower', content: [
|
||||
{ class: 'error' },
|
||||
{ class: 'buttons', content: [
|
||||
{ class: 'button submit', content: 'Add Guild' }
|
||||
] }
|
||||
] }
|
||||
]
|
||||
});
|
||||
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content add-guild">
|
||||
<div className="preview">
|
||||
<img className="icon" src={addGuildData.iconSrc} alt="icon"></img>
|
||||
<div>
|
||||
<div className="name">{addGuildData.name}</div>
|
||||
<div className="url">{addGuildData.url}</div>
|
||||
<div className="expires">{(expired ? 'Invite Expired ' : 'Invite Expires ') + moment(addGuildData.expires).fromNow()}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divider"></div>
|
||||
<div className="message-preview message">
|
||||
<div className="member-avatar"><img src="./img/loading.svg" alt="avatar"></img></div>
|
||||
<div className="right">
|
||||
<div className="header">
|
||||
<div className="member-name">{displayName}</div>
|
||||
<div className="timestamp">{moment().calendar(ElementsUtil.calendarFormats)}</div>
|
||||
</div>
|
||||
<div className="content text">What's up, gamers?</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="display-name-input" placeholder="Display Name" spellCheck="false"
|
||||
contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its types (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="avatar-input">
|
||||
<label className="avatar-upload-label button">
|
||||
Select Avatar
|
||||
<input className="avatar-upload" type="file" accept=".png,.jpg.,.jpeg" style={{ display: 'none' }}></input>
|
||||
</label>
|
||||
</div>
|
||||
<div className="lower">
|
||||
<div className="error"></div>
|
||||
<div className="buttons">
|
||||
<div className="button submit">Add Guild</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
let avatarBuff: Buffer | null;
|
||||
let defaultAvatarBuff: Buffer | null;
|
@ -10,26 +10,30 @@ import BaseElements from "./require/base-elements";
|
||||
import Q from '../q-module';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default function createCreateChannelOverlay(document: Document, q: Q, guild: CombinedGuild): HTMLElement {
|
||||
|
||||
// See also overlay-modify-channel
|
||||
|
||||
const element = BaseElements.createOverlay(document, { class: 'content submit-dialog modify-channel', content: [
|
||||
{ class: 'preview channel-title', content: [
|
||||
{ class: 'channel-icon', content: BaseElements.Q_TEXT_CHANNEL_ICON },
|
||||
{ class: 'channel-name', content: 'channel-name' },
|
||||
{ class: 'channel-flavor-divider' },
|
||||
{ class: 'channel-flavor-text', content: '' }
|
||||
] },
|
||||
{ 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: [
|
||||
{ class: 'button submit', content: 'Create Channel' }
|
||||
] }
|
||||
] }
|
||||
] });
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content submit-dialog modify-channel">
|
||||
<div className="preview channel-title">
|
||||
<div className="channel-icon">{BaseElements.TEXT_CHANNEL_ICON}</div>
|
||||
<div className="channel-name">channel-name</div>
|
||||
<div className="channel-flavor-divider"></div>
|
||||
<div className="channel-flavor-text"></div>
|
||||
</div>
|
||||
<div className="text-input channel-name" data-placeholder="channel-name" contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its typings (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="text-input channel-flavor-text" data-placeholder="Flavor Text (optional)" contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its typings (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="lower">
|
||||
<div className="error"></div>
|
||||
<div className="buttons">
|
||||
<div className="button submit">Create Channel</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
let newName = '';
|
||||
let newFlavorText: string | null = '';
|
@ -1,24 +0,0 @@
|
||||
import CombinedGuild from "../guild-combined";
|
||||
import BaseElements from "./require/base-elements";
|
||||
|
||||
export default function createCreateInviteTokenOverlay(document: Document, guild: CombinedGuild): HTMLElement {
|
||||
const element = BaseElements.createOverlay(document, { class: 'content submit-dialog', content: [
|
||||
{ class: 'role-select category-select', content: [
|
||||
{ class: 'label', content: 'Select Starting Roles' },
|
||||
{ class: 'categories', content: [
|
||||
{ class: 'category suggestion-1', content: '+Gamer' },
|
||||
{ class: 'category suggestion-2', content: '+Chodist' },
|
||||
{ class: 'category suggestion-3', content: '+Gamer' },
|
||||
{ class: 'category more', content: 'More...' },
|
||||
] }
|
||||
] },
|
||||
{ class: 'lower', content: [
|
||||
{ class: 'error' },
|
||||
{ class: 'buttons', content: [
|
||||
{ class: 'button submit', content: 'Create Invite Token' }
|
||||
] }
|
||||
] }
|
||||
] });
|
||||
|
||||
return element;
|
||||
}
|
28
src/client/webapp/elements/overlay-create-invite-token.tsx
Normal file
28
src/client/webapp/elements/overlay-create-invite-token.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import CombinedGuild from "../guild-combined";
|
||||
import BaseElements, { HTMLElementWithRemoveSelf } from "./require/base-elements";
|
||||
|
||||
import React from "react";
|
||||
|
||||
export default function createCreateInviteTokenOverlay(document: Document, guild: CombinedGuild): HTMLElementWithRemoveSelf {
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content submit-dialog">
|
||||
<div className="role-select category-select">
|
||||
<div className="label">Select Starting Roles</div>
|
||||
<div className="categories">
|
||||
<div className="category suggestion-1">+Gamer</div>
|
||||
<div className="category suggestion-2">+Chodist</div>
|
||||
<div className="category suggestion-3">+Gamer</div>
|
||||
<div className="category more">More...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="lower">
|
||||
<div className="error"></div>
|
||||
<div className="buttons">
|
||||
<div className="button submit">Create Invite Token</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
return element;
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
import BaseElements from './require/base-elements.js';
|
||||
|
||||
export default function createErrorMessageOverlay(document: Document, title: string, message: string): HTMLElement {
|
||||
return BaseElements.createOverlay(document, {
|
||||
class: 'content error-message', content: [
|
||||
{ class: 'title', content: title },
|
||||
{ class: 'message', content: message }
|
||||
]
|
||||
});
|
||||
}
|
11
src/client/webapp/elements/overlay-error-message.tsx
Normal file
11
src/client/webapp/elements/overlay-error-message.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import BaseElements, { HTMLElementWithRemoveSelf } from './require/base-elements.js';
|
||||
|
||||
export default function createErrorMessageOverlay(document: Document, title: string, message: string): HTMLElementWithRemoveSelf {
|
||||
return BaseElements.createOverlay(document, (
|
||||
<div className="content error-message">
|
||||
<div className="title">{title}</div>
|
||||
<div className="message">{message}</div>
|
||||
</div>
|
||||
));
|
||||
}
|
@ -6,64 +6,61 @@ const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import Globals from '../globals';
|
||||
|
||||
import BaseElements from './require/base-elements';
|
||||
import BaseElements, { HTMLElementWithRemoveSelf } from './require/base-elements';
|
||||
import ElementsUtil from './require/elements-util';
|
||||
|
||||
import { GuildMetadata } from '../data-types';
|
||||
import Q from '../q-module';
|
||||
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 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: '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' },
|
||||
] }
|
||||
] }
|
||||
] },
|
||||
] }
|
||||
]
|
||||
});
|
||||
import React from 'react';
|
||||
|
||||
export default function createGuildSettingsOverlay(document: Document, q: Q, guild: CombinedGuild, guildMeta: GuildMetadata): HTMLElementWithRemoveSelf {
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content display-swapper guild-settings">
|
||||
<div className="options">
|
||||
<div className="title">{guildMeta.name}</div>
|
||||
<div className="choosable chosen">Overview</div>
|
||||
<div className="choosable">Channels</div>
|
||||
<div className="choosable">Roles</div>
|
||||
<div className="choosable">Invites</div>
|
||||
</div>
|
||||
<div className="display">
|
||||
<div className="scroll">
|
||||
<div className="metadata">
|
||||
<label className="image-input-label">
|
||||
<div className="icon">
|
||||
<img className="guild-icon" src="./img/loading.svg" alt="icon"></img>
|
||||
<div className="modify"><img src="./img/pencil-icon.png" alt="modify"></img></div>
|
||||
</div>
|
||||
<input className="image-input-upload" type="file" accept=".png,.jpg,.jpeg" style={{ display: 'none' }}></input>
|
||||
</label>
|
||||
<div className="name">
|
||||
<div className="label">Guild Name</div>
|
||||
<input className="guild-name" type="text" placeholder={guildMeta.name}></input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="popup changes">
|
||||
<div className="content">
|
||||
<div className="tip">You have unsaved changes</div>
|
||||
<div className="actions">
|
||||
<div className="button perdu reset">Reset</div>
|
||||
<div className="button positive save-changes">Save Changes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="popup error">
|
||||
<div className="content">
|
||||
<div className="tip"></div>
|
||||
<div className="actions">
|
||||
<div className="button perdu close">X</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
let newIconBuff: Buffer | null = null;
|
||||
let oldName = guildMeta.name;
|
||||
@ -87,8 +84,10 @@ export default function createGuildSettingsOverlay(document: Document, q: Q, gui
|
||||
}
|
||||
|
||||
function reset() {
|
||||
q.$$$<HTMLInputElement>(element, '.image-input-upload').value = '';
|
||||
q.$$$<HTMLInputElement>(element, 'input.guild-name').value = oldName;
|
||||
(async () => {
|
||||
LOG.debug('resetting icon');
|
||||
q.$$$<HTMLImageElement>(element, '.icon img').src = await ElementsUtil.getImageBufferFromResourceFailSoftly(guild, guildMeta.iconResourceId);
|
||||
})();
|
||||
|
||||
@ -114,6 +113,7 @@ export default function createGuildSettingsOverlay(document: Document, q: Q, gui
|
||||
onCleared: () => {},
|
||||
onError: async (errMsg) => await updatePopups(errMsg),
|
||||
onLoaded: async (buff, src) => {
|
||||
LOG.debug('image loaded');
|
||||
newIconBuff = buff;
|
||||
(q.$$$(element, 'img.guild-icon') as HTMLImageElement).src = src;
|
||||
await updatePopups();
|
@ -5,24 +5,28 @@ const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import * as FileType from 'file-type'
|
||||
|
||||
import BaseElements from './require/base-elements';
|
||||
import BaseElements, { HTMLElementWithRemoveSelf } from './require/base-elements';
|
||||
import ElementsUtil from './require/elements-util';
|
||||
|
||||
import Q from '../q-module';
|
||||
import createImageContextMenu from './context-menu-img';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
export default function createImageOverlay(document: Document, q: Q, guild: CombinedGuild, resourceId: string, resourceName: string): HTMLElement {
|
||||
const element = BaseElements.createOverlay(document, { class: 'content popup-image', content: [
|
||||
{ tag: 'img', src: './img/loading.svg', alt: resourceName, title: resourceName },
|
||||
{ class: 'download', content: [
|
||||
{ class: 'info', content: [
|
||||
{ class: 'name', content: resourceName },
|
||||
{ class: 'size', content: 'Loading Size...' }
|
||||
] },
|
||||
{ class: 'button', content: 'Loading...' }
|
||||
] }
|
||||
] });
|
||||
import React from 'react';
|
||||
|
||||
export default function createImageOverlay(document: Document, q: Q, guild: CombinedGuild, resourceId: string, resourceName: string): HTMLElementWithRemoveSelf {
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content popup-image">
|
||||
<img src="./img/loading.svg" alt={resourceName} title={resourceName}></img>
|
||||
<div className="download">
|
||||
<div className="info">
|
||||
<div className="name">{resourceName}</div>
|
||||
<div className="size">Loading Size...</div>
|
||||
</div>
|
||||
<div className="button">Loading...</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
(async () => {
|
||||
try {
|
@ -6,30 +6,39 @@ const LOG = Logger.create(__filename, electronConsole);
|
||||
import { Channel } from '../data-types';
|
||||
import Globals from '../globals.js';
|
||||
|
||||
import BaseElements from './require/base-elements.js';
|
||||
import BaseElements, { HTMLElementWithRemoveSelf } from './require/base-elements.js';
|
||||
import ElementsUtil from './require/elements-util.js';
|
||||
import Q from '../q-module';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
export default function createModifyChannelOverlay(document: Document, q: Q, guild: CombinedGuild, channel: Channel): HTMLElement {
|
||||
import React from 'react';
|
||||
|
||||
export default function createModifyChannelOverlay(document: Document, q: Q, guild: CombinedGuild, channel: Channel): HTMLElementWithRemoveSelf {
|
||||
// See also overlay-create-channel
|
||||
|
||||
const element = BaseElements.createOverlay(document, { class: 'content submit-dialog modify-channel', content: [
|
||||
{ class: 'preview channel-title', content: [
|
||||
{ class: 'channel-icon', content: BaseElements.Q_TEXT_CHANNEL_ICON },
|
||||
{ class: 'channel-name', content: channel.name },
|
||||
{ class: 'channel-flavor-divider' },
|
||||
{ class: 'channel-flavor-text', content: channel.flavorText || '' }
|
||||
] },
|
||||
{ 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: [
|
||||
{ class: 'button submit', content: 'Save Changes' }
|
||||
] }
|
||||
] }
|
||||
] });
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content submit-dialog modify-channel">
|
||||
<div className="preview channel-title">
|
||||
<div className="channel-icon">{BaseElements.TEXT_CHANNEL_ICON}</div>
|
||||
<div className="channel-name">{channel.name}</div>
|
||||
<div className="channel-flavor-divider"></div>
|
||||
<div className="channel-flavor-text">{channel.flavorText || ''}</div>
|
||||
</div>
|
||||
<div className="text-input channel-name" data-placeholder="channel-name"
|
||||
contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its typings (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="text-input channel-flavor-text" data-placeholder="Flavor Text (optional)"
|
||||
contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its typings (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="lower">
|
||||
<div className="error"></div>
|
||||
<div className="buttons">
|
||||
<div className="button submit">Save Changes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
q.$$$(element, '.text-input.channel-name').innerText = channel.name;
|
||||
q.$$$(element, '.text-input.channel-flavor-text').innerText = channel.flavorText || '';
|
||||
|
||||
let newName = channel.name;
|
||||
let newFlavorText = channel.flavorText;
|
@ -4,7 +4,7 @@ const electronConsole = electronRemote.getGlobal('console') as Console;
|
||||
import Logger from '../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import BaseElements from './require/base-elements';
|
||||
import BaseElements, { HTMLElementWithRemoveSelf } from './require/base-elements';
|
||||
import ElementsUtil from './require/elements-util';
|
||||
|
||||
import Globals from '../globals';
|
||||
@ -14,27 +14,46 @@ import createTextMessage from './msg-txt';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
import { ConnectionInfo } from '../data-types';
|
||||
|
||||
export default function createPersonalizeOverlay(document: Document, q: Q, guild: CombinedGuild, connection: ConnectionInfo): HTMLElement {
|
||||
const element = BaseElements.createOverlay(document, {
|
||||
class: 'content submit-dialog personalize', content: [
|
||||
createTextMessage(q, guild, { id: 'test-message', member: connection, sent: new Date(), text: 'Example Message' }),
|
||||
{ class: 'text-input', placeholder: 'New Display Name',
|
||||
spellcheck: 'false', contenteditable: 'plaintext-only', content: connection.displayName },
|
||||
{ class: 'image-input avatar-input', content: [
|
||||
{ tag: 'label', class: 'image-input-label avatar-input-label button', content: [
|
||||
'Select New Avatar',
|
||||
{ class: 'image-input-upload avatar-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' }
|
||||
] }
|
||||
] }
|
||||
]
|
||||
});
|
||||
import React from 'react';
|
||||
import moment from 'moment';
|
||||
|
||||
export default function createPersonalizeOverlay(document: Document, q: Q, guild: CombinedGuild, connection: ConnectionInfo): HTMLElementWithRemoveSelf {
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content submit-dialog personalize">
|
||||
<div className="message">
|
||||
<div className="member-avatar">
|
||||
<img src="./img/loading.svg" alt={connection.displayName}></img>
|
||||
</div>
|
||||
<div className="right">
|
||||
<div className="header">
|
||||
<div className="member-name">{connection.displayName}</div>
|
||||
<div className="timestamp">{moment(new Date()).calendar(ElementsUtil.calendarFormats)}</div>
|
||||
</div>
|
||||
<div className="content text">Example Message</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-input display-name-input" data-placeholder="New Display Name"
|
||||
contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its typings (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="image-input avatar-input">
|
||||
<label className="image-input-label avatar-input-label button">
|
||||
Select New Avatar
|
||||
<input className="image-input-upload avatar-upload" type="file" accept=".png,.jpg,.jpeg" style={{ display: 'none' }}></input>
|
||||
</label>
|
||||
</div>
|
||||
<div className="lower">
|
||||
<div className="error"></div>
|
||||
<div className="buttons">
|
||||
<div className="button submit">Save Changes</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
q.$$$(element, '.text-input.display-name-input').innerText = connection.displayName;
|
||||
(async () => {
|
||||
(q.$$$(element, '.member-avatar img') as HTMLImageElement).src =
|
||||
await ElementsUtil.getImageBufferFromResourceFailSoftly(guild, connection.avatarResourceId);
|
||||
})();
|
||||
|
||||
let newAvatarBuffer: Buffer | null = null;
|
||||
BaseElements.bindImageUploadEvents(q.$$$(element, '.avatar-upload') as HTMLInputElement, {
|
@ -13,12 +13,14 @@ import { Member } from '../data-types';
|
||||
import Q from '../q-module';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default function createTokenLogOverlay(document: Document, q: Q, guild: CombinedGuild): HTMLElement {
|
||||
const element = BaseElements.createOverlay(document, {
|
||||
class: 'content token-log', content: [
|
||||
{ class: 'tokens', content: { tag: 'img', src: './img/loading.svg', alt: 'loading...' } },
|
||||
]
|
||||
});
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content token-log">
|
||||
<div className="tokens"><img src="./img/loading.svg" alt="Loading..."></img></div>
|
||||
</div>
|
||||
));
|
||||
|
||||
(async () => {
|
||||
Util.withPotentialErrorWarnOnCancel(q, {
|
@ -9,11 +9,16 @@ import Q from '../q-module';
|
||||
import createUploadOverlayFromDataTransferItem from './overlay-upload-datatransfer';
|
||||
import CombinedGuild from '../guild-combined';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
export default function createUploadDropTarget(document: Document, q: Q, guild: CombinedGuild, channel: Channel): HTMLElement {
|
||||
const element = BaseElements.createOverlay(document, { class: 'content drop-target', content: [
|
||||
// TODO: icon?
|
||||
{ class: 'message', content: 'Upload to #' + channel.name }
|
||||
] });
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content drop-target">
|
||||
{/* TODO: icon? */}
|
||||
<div className="message">Upload to #{channel.name}</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
element.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
@ -250,12 +250,18 @@ export default class BaseElements {
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static createOverlay(document: Document, content: any): HTMLElementWithRemoveSelf {
|
||||
static createOverlay(document: Document, content: JSX.Element): HTMLElementWithRemoveSelf {
|
||||
const q = new Q(document);
|
||||
|
||||
let wasDownInternal = false; // because 'click' fires on the overlay element anyway
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const element = q.create({ class: 'overlay', content: content }) as any;
|
||||
const element: HTMLElementWithRemoveSelf = ReactHelper.createElementFromJSX(<div className="overlay">{content}</div>) as HTMLElementWithRemoveSelf;
|
||||
element.removeSelf = () => {
|
||||
if (element.parentElement) {
|
||||
element.parentElement.removeChild(element);
|
||||
}
|
||||
window.removeEventListener('keydown', onKeyEscape);
|
||||
}
|
||||
const onKeyEscape = (e: KeyboardEvent) => {
|
||||
if (e.key == 'Escape') {
|
||||
element.removeSelf();
|
||||
@ -269,12 +275,7 @@ export default class BaseElements {
|
||||
}
|
||||
element.removeSelf();
|
||||
});
|
||||
element.removeSelf = () => {
|
||||
if (element.parentElement) {
|
||||
element.parentElement.removeChild(element);
|
||||
}
|
||||
window.removeEventListener('keydown', onKeyEscape);
|
||||
}
|
||||
|
||||
q.$$$(element, '.content').addEventListener('click', (e) => {
|
||||
e.stopPropagation(); // prevent the element from closing if the content is clicked on
|
||||
});
|
||||
@ -290,23 +291,25 @@ export default class BaseElements {
|
||||
|
||||
const { guild, channel, resourceName, resourceBuffFunc, resourceSizeFunc } = props;
|
||||
|
||||
const element = BaseElements.createOverlay(document, { class: 'content upload', content: [
|
||||
{ class: 'title', content: [
|
||||
{ tag: 'img', src: './img/loading.svg', alt: resourceName },
|
||||
{ class: 'right', content: [
|
||||
{ class: 'name', content: resourceName },
|
||||
{ class: 'size', content: '? B' }
|
||||
] }
|
||||
] },
|
||||
{ class: 'text-input', placeholder: 'Add a comment (optional)', contenteditable: 'plaintext-only' },
|
||||
{ class: 'lower', content: [
|
||||
{ class: 'error' },
|
||||
{ class: 'buttons', content: [
|
||||
{ class: 'button upload', content: 'Upload to #' + channel.name }
|
||||
] }
|
||||
] }
|
||||
const element = BaseElements.createOverlay(document, (
|
||||
<div className="content upload">
|
||||
<div className="title">
|
||||
<img src="./img/loading.svg" alt={resourceName}></img>
|
||||
<div className="right">
|
||||
<div className="name">{resourceName}</div>
|
||||
<div className="size">? B</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-input" data-placeholder="Add a comment (optional)" contentEditable={'plaintext-only' as unknown as boolean /* React doesn't have plaintext-only in its typings (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/54779) */}></div>
|
||||
<div className="lower">
|
||||
<div className="error"></div>
|
||||
<div className="buttons">
|
||||
<div className="button upload">Upload to #{channel.name}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
] });
|
||||
q.$$$(element, '.text-input').addEventListener('keydown', async (e) => {
|
||||
if (e.key == 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
|
@ -9,6 +9,7 @@ import Logger from '../../../../logger/logger';
|
||||
const LOG = Logger.create(__filename, electronConsole);
|
||||
|
||||
import * as FileType from 'file-type';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
import Util from '../../util';
|
||||
import Globals from '../../globals';
|
||||
@ -17,11 +18,6 @@ import { ShouldNeverHappenError } from '../../data-types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// TODO: pass-through Globals in init function
|
||||
// alignment: {
|
||||
// centerY: 'top'
|
||||
// left: 'right + 20'
|
||||
// }
|
||||
interface IAlignment {
|
||||
left?: string;
|
||||
centerX?: string;
|
||||
@ -37,7 +33,6 @@ interface IHTMLElementWithRemovalType extends HTMLElement {
|
||||
|
||||
interface SimpleQElement {
|
||||
tag: 'span',
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
content: (SimpleQElement | string)[],
|
||||
class: string | null
|
||||
}
|
||||
@ -124,11 +119,8 @@ export default class ElementsUtil {
|
||||
}
|
||||
|
||||
// creates <span class="bold"/"italic"/"bold italic"/"underline"> spans to format the text (all in q.js element markup that gets converted to jsx [aka hypergaming])
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
static parseMessageText(text: string): JSX.Element {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const obj: SimpleQElement = { tag: 'span', content: [] as (SimpleQElement | string)[], class: null };
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const obj: SimpleQElement = { tag: 'span', content: [], class: null };
|
||||
const stack: SimpleQElement[] = [ obj ];
|
||||
let idx = 0;
|
||||
function makeEscape(regex: RegExp, len: number, str: string): { matcher: RegExp, response: ((i: number) => void)} { // function for readability
|
||||
@ -152,8 +144,7 @@ export default class ElementsUtil {
|
||||
// TODO: optimise out empty elements
|
||||
stack.pop();
|
||||
} else { // italic begins
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const obj: SimpleQElement = { tag: 'span', class: cls, content: [] as any[] }
|
||||
const obj: SimpleQElement = { tag: 'span', class: cls, content: [] }
|
||||
top.content.push(obj);
|
||||
stack.push(obj);
|
||||
}
|
||||
@ -195,7 +186,7 @@ export default class ElementsUtil {
|
||||
return qjsObj
|
||||
} else {
|
||||
const content = qjsObj.content.map(qjsElement => createReactElement(qjsElement));
|
||||
return React.createElement(qjsObj.tag, { className: qjsObj.class }, content);
|
||||
return React.createElement(qjsObj.tag, { key: uuid.v4(), className: qjsObj.class }, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user