Compare commits

...

7 commits

Author SHA1 Message Date
74f116bfac Altered the positioning for the pickers. 2024-06-27 18:11:51 +00:00
b0ad0108f8 Added toggle for :visited links. 2024-06-27 16:01:28 +00:00
cfeaae19a7 Moved markup__link styling out of the theme definition cuz its identical anyway. 2024-06-27 15:48:30 +00:00
8e162e4163 Make colour and emoticon pickers follow selected theme. 2024-06-27 15:43:50 +00:00
81c06d4289 Rewrote emoticon picker. 2024-06-26 23:07:13 +00:00
5dc701cca4 Added dropdown to select state of the markup tags list.
Also removed the button from the input bar, you can now select it from the settings side menu under Text -> Show markup buttons.
2024-06-26 15:36:02 +00:00
48a98e7448 Removed AccentReload method. 2024-06-26 14:06:01 +00:00
21 changed files with 484 additions and 236 deletions

View file

@ -1,17 +1,12 @@
.colpick {
accent-color: var(--colpick-colour, #000);
border-radius: 5px;
border: 2px solid var(--colpick-colour, #000);
padding: 3px;
padding: 2px;
display: flex;
flex-direction: column;
position: absolute;
background-color: #444;
color: #fff;
box-shadow: 0 3px 10px #000;
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
font-size: 12px;
line-height: 20px;
background-color: var(--theme-colour-input-menu-background);
box-shadow: 0 2px 10px var(--theme-colour-input-menu-box-shadow);
border: 1px solid var(--theme-colour-sidebar-background);
}
.colpick-tabbed {
@ -19,40 +14,36 @@
margin-bottom: 3px;
}
.colpick-tabbed-container {
border: 2px solid var(--colpick-colour, #000);
border-radius: 5px 5px 0 0;
height: 234px;
overflow: auto;
scrollbar-width: thin;
background-color: var(--theme-colour-sidebar-background);
}
.colpick-tabbed-list {
background-color: #222;
border-radius: 0 0 5px 5px;
overflow: auto;
}
.colpick-tab-button {
background: #333;
color: #fff;
background: transparent;
color: inherit;
border-radius: 0;
border-width: 0;
display: inline-block;
padding: 3px 5px;
padding: 4px 10px;
height: 24px;
margin-right: 1px;
cursor: pointer;
transition: background .1s;
}
.colpick-tab-button:hover {
background: #444;
border-color: #444;
}
.colpick-tab-button:hover,
.colpick-tab-button:focus {
border-color: #fff;
background: #fff4;
}
.colpick-tab-button:active {
background: #0004;
}
.colpick-tab-button-active {
background: var(--colpick-colour, #000);
border-color: var(--colpick-colour, #000);
color: var(--colpick-text, #000);
background: var(--theme-colour-sidebar-background) !important;
}
.colpick-middle-row {
@ -71,7 +62,6 @@
display: block;
width: 60px;
height: 60px;
border-radius: 5px;
background: var(--colpick-colour, #000);
}
@ -96,16 +86,17 @@
}
.colpick-values-child-input {
display: inline-block;
border: 1px solid #222;
background: #333;
border: 1px solid var(--theme-colour-settings-input-border);
background: var(--theme-colour-settings-input-background);
border-radius: 0;
color: #fff;
padding: 1px;
color: inherit;
padding: 2px;
width: 100px;
outline-style: none;
transition: border .1s, background .1s;
}
.colpick-values-child-input:focus {
border-color: #777;
background: var(--theme-colour-settings-input-focus);
}
.colpick-buttons-container {
@ -118,23 +109,27 @@
}
.colpick-buttons-button {
display: inline-block;
background: var(--theme-colour-settings-input-background);
border: 1px solid var(--theme-colour-settings-input-border);
border-radius: 5px;
background: #222;
border-width: 0;
color: #fff;
color: inherit;
font-family: inherit;
font-size: 1.1em;
line-height: 1.2em;
padding: 3px 10px;
margin-left: 5px;
cursor: pointer;
transition: background .1s;
}
.colpick-buttons-button:hover {
background: var(--theme-colour-settings-input-focus);
}
.colpick-buttons-button:focus {
box-shadow: 0 0 0 1px #000, inset 0 0 0 1px #fff;
}
.colpick-buttons-button-submit {
font-weight: 700;
color: var(--colpick-text, #fff);
background: var(--colpick-colour, #000);
}
.colpick-tab-container {}
@ -156,9 +151,9 @@
border: 0;
width: 42px;
height: 42px;
border-radius: 5px;
border-radius: 0;
text-decoration: none;
color: #fff;
color: inherit;
cursor: pointer;
}
.colpick-presets-option:focus {
@ -218,16 +213,17 @@
}
.colpick-slider-value-input {
display: inline-block;
border: 1px solid #222;
background: #333;
border: 1px solid var(--theme-colour-settings-input-border);
background: var(--theme-colour-settings-input-background);
border-radius: 0;
color: #fff;
padding: 1px;
color: inherit;
padding: 2px;
width: 50px;
outline-style: none;
transition: border .1s, background .1s;
}
.colpick-slider-value-input:focus {
border-color: #777;
background: var(--theme-colour-settings-input-focus);
}
.colpick-slider-red .colpick-slider-value-input:focus {
border-color: #f00;

103
src/mami.css/emopick.css Normal file
View file

@ -0,0 +1,103 @@
.emopick {
padding: 2px;
display: flex;
flex-direction: column;
position: absolute;
background-color: var(--theme-colour-input-menu-background);
box-shadow: 0 2px 10px var(--theme-colour-input-menu-box-shadow);
border: 1px solid var(--theme-colour-sidebar-background);
max-width: 336px;
width: 100%;
}
.emopick-emote {
background: transparent;
border: 0;
border-radius: 0;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background .1s;
}
.emopick-emote:hover,
.emopick-emote:focus {
background: #fff4;
}
.emopick-emote:active {
background: #0004;
}
.emopick-emote img {
max-width: 100%;
max-height: 100%;
display: block;
image-rendering: crisp-edges;
}
.emopick-list {
max-height: 250px;
height: 100%;
overflow: auto;
scrollbar-width: thin;
background: var(--theme-colour-sidebar-background);
}
.emopick-list-scroll {
display: flex;
flex-wrap: wrap;
gap: 2px;
padding: 2px;
}
.emopick-search {
display: flex;
margin: 2px 0;
}
.emopick-search-input {
flex-grow: 1;
flex-shrink: 1;
padding: 4px;
border: 0;
border-radius: 0;
color: inherit;
background: var(--theme-colour-sidebar-background);
font-size: inherit;
font-family: inherit;
}
.emopick-actions {
display: flex;
justify-content: space-between;
}
.emopick-action-toggle {
display: block;
}
.emopick-action-toggle-box {
margin: 0 2px;
}
.emopick-action-toggle-label {
margin: 0 2px;
}
.emopick-action-button {
display: inline-block;
background: var(--theme-colour-settings-input-background);
border: 1px solid var(--theme-colour-settings-input-border);
border-radius: 5px;
color: inherit;
font-family: inherit;
font-size: 1.1em;
line-height: 1.2em;
padding: 3px 10px;
margin-left: 5px;
cursor: pointer;
transition: background .1s;
}
.emopick-action-button:hover {
background: var(--theme-colour-settings-input-focus);
}
.emopick-action-button:focus {
box-shadow: 0 0 0 1px #000, inset 0 0 0 1px #fff;
}

View file

@ -89,6 +89,7 @@ a:hover {
@include youare.css;
@include colpick.css;
@include emopick.css;
@include themes/archaic.css;
@include themes/blue.css;

View file

@ -9,9 +9,16 @@
}
.markup__link {
color: #1e90ff;
text-decoration: none
}
.markup__link:hover {
.markup__link:hover,
.markup__link:focus {
text-decoration: underline
}
.chat:not(.mami-do-not-mark-links-as-visited) .markup__link--visited,
.chat:not(.mami-do-not-mark-links-as-visited) .markup__link:visited {
color: #6B4F80;
}

View file

@ -27,13 +27,19 @@ select {
margin-bottom: 6px;
}
.setting__container--select .setting__input,
.setting__container--number .setting__input,
.setting__container--text .setting__input {
width: 100%;
font-size: 1.1em;
line-height: 1.4em;
font-family: Verdana, Tahoma, Geneva, Arial, Helvetica, sans-serif
font-family: inherit;
}
.setting__container--select .setting__input {
width: 100%;
font-size: inherit;
font-family: inherit;
padding: 2px;
}
.setting__container--range .setting__input {

View file

@ -165,11 +165,3 @@
.umi--archaic .markup__button:active {
background-color: #222
}
.umi--archaic .markup__link {
color: #1e90ff;
}
.umi--archaic .markup__link--visited,
.umi--archaic .markup__link:visited {
color: #6B4F80;
}

View file

@ -165,11 +165,3 @@
.umi--blue .markup__button:active {
background-color: #0e507a
}
.umi--blue .markup__link {
color: #1e90ff;
}
.umi--blue .markup__link--visited,
.umi--blue .markup__link:visited {
color: #6B4F80;
}

View file

@ -165,11 +165,3 @@
.umi--dark .markup__button:active {
background-color: #3a3a3a
}
.umi--dark .markup__link {
color: #1e90ff;
}
.umi--dark .markup__link--visited,
.umi--dark .markup__link:visited {
color: #6B4F80;
}

View file

@ -165,11 +165,3 @@
.umi--light .markup__button:active {
background-color: #c9c9c9
}
.umi--light .markup__link {
color: #1e90ff;
}
.umi--light .markup__link--visited,
.umi--light .markup__link:visited {
color: #6B4F80;
}

View file

@ -165,11 +165,3 @@
.umi--purple .markup__button:active {
background-color: #604c74
}
.umi--purple .markup__link {
color: #1e90ff;
}
.umi--purple .markup__link--visited,
.umi--purple .markup__link:visited {
color: #6B4F80;
}

View file

@ -42,7 +42,7 @@ const MamiColourPicker = function(options) {
let tabsElem, tabsContainer, tabsList;
let values, buttons;
const html = <form class="colpick" style={{ zIndex: '9001' }} onsubmit={ev => { ev.preventDefault(); runResolve(); return false; }}>
const html = <form class="colpick" style="z-index: 9001;" onsubmit={ev => { ev.preventDefault(); runResolve(); return false; }}>
{tabsElem = <div class="colpick-tabbed">
{tabsContainer = <div class="colpick-tabbed-container"/>}
{tabsList = <div class="colpick-tabbed-list"/>}
@ -58,7 +58,7 @@ const MamiColourPicker = function(options) {
</div>
</form>;
const close = () => html.parentNode.removeChild(html);
const close = () => html.classList.add('hidden');
const setColour = (raw, mask) => {
raw = typeof raw === 'number' ? (parseInt(raw) & 0xFFFFFF) : 0;
@ -79,15 +79,15 @@ const MamiColourPicker = function(options) {
if(typeof pos !== 'object')
throw 'pos must be an object';
html.style.top = 'y' in pos && pos.y >= 0 ? `${pos.y}px` : null;
html.style.left = 'x' in pos && pos.x >= 0 ? `${pos.x}px` : '';
for(const attr of ['top', 'left', 'right', 'bottom'])
html.style[attr] = typeof pos[attr] === 'number' ? `${pos[attr]}px` : null;
};
const tabs = new MamiTabsControl({
onAdd: ctx => {
const name = ctx.info.name,
containerName = `colpick-tab-${name}-container`,
buttonName = `colpick-tab-${name}-button`;
const name = ctx.info.name;
const containerName = `colpick-tab-${name}-container`;
const buttonName = `colpick-tab-${name}-button`;
needsColour.push(ctx.info);
ctx.info.onChange(setColour);
@ -107,11 +107,11 @@ const MamiColourPicker = function(options) {
onSwitch: ctx => {
if(ctx.from !== undefined) {
ctx.from.elem.classList.toggle('colpick-tab-container-inactive', true);
$q(`.colpick-tab-${ctx.from.info.name}-button`).classList.toggle('colpick-tab-button-active', false);
tabsList.querySelector(`.colpick-tab-${ctx.from.info.name}-button`).classList.toggle('colpick-tab-button-active', false);
}
ctx.elem.classList.toggle('colpick-tab-container-inactive', false);
$q(`.colpick-tab-${ctx.info.name}-button`).classList.toggle('colpick-tab-button-active', true);
tabsList.querySelector(`.colpick-tab-${ctx.info.name}-button`).classList.toggle('colpick-tab-button-active', true);
},
});
@ -166,8 +166,25 @@ const MamiColourPicker = function(options) {
get colour() { return colour; },
set colour(value) { setColour(value); },
setPosition: setPosition,
close: close,
close: runReject,
dialog: pos => {
html.classList.remove('hidden');
if(pos instanceof MouseEvent) {
const ev = pos;
pos = {};
const mbb = html.getBoundingClientRect();
const bbb = ev.target.getBoundingClientRect();
const pbb = html.parentNode.getBoundingClientRect();
pos.left = bbb.left;
pos.bottom = pbb.height - bbb.top;
if(pos.left + mbb.width >= pbb.width)
pos.left = 0;
}
if(pos !== undefined)
setPosition(pos);
@ -176,22 +193,5 @@ const MamiColourPicker = function(options) {
promiseReject = reject;
});
},
suggestPosition: mouseEvent => {
let x = 10, y = 10;
if(html.parentNode.clientWidth > 340) {
x = mouseEvent.clientX;
y = mouseEvent.clientY;
const bb = html.getBoundingClientRect();
if(y > bb.height + 20)
y -= bb.height;
if(x > html.parentNode.clientWidth - bb.width - 20)
x -= bb.width;
}
return { x: x, y: y };
},
};
};

View file

@ -37,6 +37,14 @@ const MamiEmotes = (function() {
if(emote.minRank <= minRank)
callback(emote);
},
all: minRank => {
const items = [];
for(const emote of emotes)
if(emote.minRank <= minRank)
items.push(emote);
return items;
},
findByName: function(minRank, name, returnString) {
const found = [];
for(const emote of emotes)

View file

@ -0,0 +1,146 @@
#include args.js
#include utility.js
const MamiEmotePicker = function(args) {
args = MamiArgs('args', args, define => {
define('getEmotes').type('function').done();
define('setKeepOpenOnPick').type('function').done();
define('onPick').type('function').done();
define('onClose').type('function').done();
});
let emotes;
let listElem, searchElem, keepOpenToggleElem;
const html = <div class="emopick" style="z-index: 9001;" onfocusout={ev => {
if(!keepOpenToggleElem.checked && !html.contains(ev.relatedTarget))
close();
}} tabindex="0">
<div class="emopick-list" tabindex="0">
{listElem = <div class="emopick-list-scroll" tabindex="0"/>}
</div>
<div class="emopick-search" tabindex="0">
{searchElem = <input class="emopick-search-input" type="search" placeholder="Filter..." tabindex="0" />}
</div>
<div class="emopick-actions" tabindex="0">
<label class="emopick-action-toggle" tabindex="0">
{keepOpenToggleElem = <input type="checkbox" class="emopick-action-toggle-box" onchange={() => {
if(args.setKeepOpenOnPick !== undefined)
args.setKeepOpenOnPick(keepOpenToggleElem.checked);
}} />}
<span class="emopick-action-toggle-label">Keep picker open</span>
</label>
<button class="emopick-action-button" type="button" onclick={() => { close(); }}>Close</button>
</div>
</div>;
const buildList = () => {
$rc(listElem);
for(const emote of emotes)
listElem.appendChild(<button class="emopick-emote" type="button" data-strings={emote.strings.join(' ')} onclick={() => {
args.onPick(emote);
if(!keepOpenToggleElem.checked)
close();
}}>
<img src={emote.url} alt={`:${emote.strings[0]}:`} />
</button>);
};
searchElem.addEventListener('keyup', ev => {
const elems = Array.from(listElem.children);
if(ev.key === 'Escape') {
close();
return;
}
if(ev.key === 'Enter' || ev.key === 'NumpadEnter') {
for(const elem of elems)
if(!elem.classList.contains('hidden')) {
elem.click();
break;
}
searchElem.focus();
return;
}
for(const elem of elems) {
const filter = searchElem.value.trim();
let hidden = false;
if(filter !== '') {
hidden = true;
const strings = elem.dataset.strings.split(' ');
for(const string of strings)
if(string.includes(filter)) {
hidden = false;
break;
}
}
elem.classList.toggle('hidden', hidden);
}
});
let promiseResolve;
const close = () => {
if(promiseResolve !== undefined) {
promiseResolve();
promiseResolve = undefined;
}
if(args.onClose !== undefined)
args.onClose();
html.classList.add('hidden');
};
const setPosition = pos => {
if(typeof pos !== 'object')
throw 'pos must be an object';
for(const attr of ['top', 'left', 'right', 'bottom'])
html.style[attr] = typeof pos[attr] === 'number' ? `${pos[attr]}px` : null;
};
return {
get element() { return html; },
get keepOpenOnPick() { return keepOpenToggleElem.checked; },
set keepOpenOnPick(value) { keepOpenToggleElem.checked = value; },
setPosition: setPosition,
close: close,
dialog: pos => {
emotes = args.getEmotes();
buildList();
html.classList.remove('hidden');
if(pos instanceof MouseEvent) {
const ev = pos;
pos = {};
const mbb = html.getBoundingClientRect();
const bbb = ev.target.getBoundingClientRect();
const pbb = html.parentNode.getBoundingClientRect();
pos.right = pbb.width - bbb.left;
pos.bottom = pbb.height - bbb.top;
if(pos.right + mbb.width > pbb.width)
pos.right = 0;
else if(pos.right > mbb.width)
pos.right -= mbb.width;
else
pos.right -= bbb.width;
}
if(pos !== undefined)
setPosition(pos);
searchElem.focus();
return new Promise(resolve => { promiseResolve = resolve; });
},
};
};

View file

@ -11,6 +11,7 @@ window.Umi = { UI: {} };
#include events.js
#include mobile.js
#include mszauth.js
#include themes.js
#include txtrigs.js
#include uniqstr.js
#include users.js
@ -22,6 +23,7 @@ window.Umi = { UI: {} };
#include controls/ping.jsx
#include controls/views.js
#include eeprom/eeprom.js
#include emotes/picker.jsx
#include notices/baka.jsx
#include notices/youare.jsx
#include settings/backup.js
@ -146,9 +148,11 @@ const MamiInit = async args => {
settings.define('dumpPackets').default(FUTAMI_DEBUG).create();
settings.define('dumpEvents').default(FUTAMI_DEBUG).create();
settings.define('marqueeAllNames').default(false).create();
settings.define('tmpDisableOldThemeSys').default(false).critical().create();
settings.define('dbgAnimDurationMulti').default(1).min(0).max(10).create();
settings.define('newLineOnEnter').default(false).create();
settings.define('showMarkupSelector').type(['always', 'focus', 'never']).default('focus').create();
settings.define('keepEmotePickerOpen').default(true).create();
settings.define('doNotMarkLinksAsVisited').default(false).create();
const noNotifSupport = !('Notification' in window);
settings.define('enableNotifications').default(false).immutable(noNotifSupport).critical().create();
@ -228,13 +232,8 @@ const MamiInit = async args => {
MamiEmotes.loadLegacy(emotes);
} catch(ex) {
console.error('Failed to load emoticons.', ex);
} finally {
// this is currently called in the sock chat handlers
// does a permissions check which it can't do at this point
//Umi.UI.Emoticons.Init();
}
const onHashChange = () => {
if(location.hash === '#reset') {
settings.clear(true);
@ -292,13 +291,22 @@ const MamiInit = async args => {
const layout = new Umi.UI.ChatLayout(sidebar);
await ctx.views.unshift(layout);
Umi.UI.View.AccentReload();
Umi.UI.Hooks.AddHooks();
settings.watch('style', ev => { if(!ev.detail.initial) Umi.UI.View.AccentReload(); });
settings.watch('compactView', ev => { if(!ev.detail.initial) Umi.UI.View.AccentReload(); });
settings.watch('preventOverflow', ev => args.parent.classList.toggle('prevent-overflow', ev.detail.value));
settings.watch('tmpDisableOldThemeSys', ev => { if(!ev.detail.initial) Umi.UI.View.AccentReload(); });
settings.watch('style', ev => {
for(const className of layout.getElement().classList)
if(className.startsWith('umi--'))
layout.getElement().classList.remove(className);
layout.getElement().classList.add(`umi--${ev.detail.value}`);
UmiThemeApply(ev.detail.value);
});
settings.watch('compactView', ev => {
layout.getElement().classList.toggle('chat--compact', ev.detail.value);
layout.getInterface().getMessageList().getElement().classList.toggle('chat--compact', ev.detail.value);
});
settings.watch('preventOverflow', ev => { args.parent.classList.toggle('prevent-overflow', ev.detail.value); });
settings.watch('doNotMarkLinksAsVisited', ev => { layout.getInterface().getMessageList().getElement().classList.toggle('mami-do-not-mark-links-as-visited', ev.detail.value); });
settings.watch('minecraft', ev => {
if(ev.detail.initial && ev.detail.value === 'no')
@ -433,11 +441,18 @@ const MamiInit = async args => {
sbSettings.category(category => {
category.header('Interface');
category.setting('style').title('Style').type('select').options(() => Umi.UI.View.AccentColours).done();
category.setting('style').title('Theme').type('select').options(() => {
const themes = {};
for(const theme of UmiThemes)
themes[theme.id] = theme.name;
return themes;
}).done();
category.setting('compactView').title('Use compact view').done();
category.setting('autoScroll').title('Enable auto scroll').done();
category.setting('closeTabConfirm').title('Confirm tab close').done();
category.setting('autoCloseUserContext').title('Auto-close user menus').done();
category.setting('doNotMarkLinksAsVisited').title("Don't mark links as visited").done();
});
sbSettings.category(category => {
category.header('Text');
@ -446,7 +461,15 @@ const MamiInit = async args => {
category.setting('eepromAutoInsert').title('Auto-insert uploads').done();
category.setting('autoEmbedV1').title('Auto-embed media').done();
category.setting('autoEmbedPlay').title('Auto-play embedded media').done();
category.setting('keepEmotePickerOpen').title('Keep emoticon picker open').done();
category.setting('newLineOnEnter').title('Swap Enter and Shift+Enter behaviour').done();
category.setting('showMarkupSelector').title('Show markup buttons').type('select').options(() => {
return {
'always': 'Always show',
'focus': 'Show when focussed',
'never': 'Never show',
};
}).done();
});
sbSettings.category(category => {
category.header('Notifications');
@ -536,7 +559,6 @@ const MamiInit = async args => {
MamiEmotes.clear();
MamiEmotes.loadLegacy(emotes);
} finally {
Umi.UI.Emoticons.Init();
button.textContent = textOrig;
button.disabled = false;
}
@ -599,7 +621,6 @@ const MamiInit = async args => {
category.setting('dumpPackets').title('Dump packets to console').done();
category.setting('dumpEvents').title('Dump events to console').done();
category.setting('marqueeAllNames').title('Apply marquee on everyone').done();
category.setting('tmpDisableOldThemeSys').title('Disable old theme system').done();
category.setting('dbgAnimDurationMulti').title('Animation multiplier').type('range').done();
category.button('Test kick/ban notice', async button => {
button.disabled = true;
@ -647,8 +668,39 @@ const MamiInit = async args => {
const sbActPing = new MamiSidebarActionPing(pingIndicator, ctx.msgbox);
sidebar.createAction(sbActPing);
Umi.UI.InputMenus.Add('markup', 'BB Code');
Umi.UI.InputMenus.Add('emotes', 'Emoticons');
Umi.UI.InputMenus.Add('markup');
Umi.UI.Markup.SetPickerTarget(layout.getElement());
let emotePicker, emotePickerVisible = false;
Umi.UI.InputMenus.AddButton('emotes', 'Emoticons', ev => {
if(emotePicker === undefined) {
emotePicker = new MamiEmotePicker({
onClose: () => { emotePickerVisible = false; },
onPick: emote => {
const emoteStr = `:${emote.strings[0]}:`;
Umi.UI.View.EnterAtCursor(emoteStr);
Umi.UI.View.SetPosition(Umi.UI.View.GetPosition() + emoteStr.length);
Umi.UI.View.SetPosition(Umi.UI.View.GetPosition(), true);
Umi.UI.View.Focus();
},
getEmotes: () => MamiEmotes.all(Umi.User.getCurrentUser().perms.rank),
setKeepOpenOnPick: value => { settings.set('keepEmotePickerOpen', value); },
});
layout.getElement().appendChild(emotePicker.element);
settings.watch('keepEmotePickerOpen', ev => { emotePicker.keepOpenOnPick = ev.detail.value; });
}
if(emotePickerVisible) {
emotePicker.close();
} else {
emotePickerVisible = true;
emotePicker.dialog(ev);
}
});
settings.watch('showMarkupSelector', ev => {
Umi.UI.InputMenus.Toggle('markup', ev.detail.value === 'always');
});
let doUpload;
ctx.eeprom = new MamiEEPROM(futami.get('eeprom2'), MamiMisuzuAuth.getLine);

View file

@ -104,7 +104,6 @@ const MamiSockChatHandlers = function(
sbUsers.createEntry(ev.detail.user);
Umi.UI.Markup.Reset();
Umi.UI.Emoticons.Init();
Umi.Parsing.Init();
if(ctx.views.count > 1)

View file

@ -1,41 +1,12 @@
#include emotes.js
#include utility.js
#include ui/input-menus.js
#include ui/view.js
Umi.UI.Emoticons = (function() {
return {
Init: function() {
const menu = Umi.UI.InputMenus.Get('emotes');
menu.innerHTML = '';
MamiEmotes.forEach(Umi.User.getCurrentUser().perms.rank, function(emote) {
menu.appendChild($e({
tag: 'button',
attrs: {
type: 'button',
className: 'emoticon emoticon--button',
title: emote.strings[0],
dataset: {
umiEmoticon: ':' + emote.strings[0] + ':',
},
onclick: 'Umi.UI.Emoticons.Insert(this)',
},
child: {
tag: 'img',
attrs: {
className: 'emoticon',
src: emote.url,
alt: emote.strings[0],
},
}
}));
});
},
Parse: function(element, message) {
Parse: function(element, author) {
let inner = element.innerHTML;
MamiEmotes.forEach(message?.author?.perms?.rank ?? 0, function(emote) {
MamiEmotes.forEach(author?.perms?.rank ?? 0, function(emote) {
const image = $e({
tag: 'img',
attrs: {
@ -54,12 +25,5 @@ Umi.UI.Emoticons = (function() {
element.innerHTML = inner;
},
Insert: function(sender) {
const emoticon = sender.getAttribute('data-umi-emoticon');
Umi.UI.View.EnterAtCursor(sender.getAttribute('data-umi-emoticon'));
Umi.UI.View.SetPosition(Umi.UI.View.GetPosition() + emoticon.length);
Umi.UI.View.SetPosition(Umi.UI.View.GetPosition(), true);
Umi.UI.View.Focus();
},
};
})();

View file

@ -11,15 +11,30 @@ Umi.UI.Hooks = (function() {
sendMessage = sendMessageFunc;
},
AddHooks: function() {
const msgForm = $i('umi-msg-form');
const msgText = $i('umi-msg-text');
window.addEventListener('keydown', function(ev) {
if((ev.ctrlKey && ev.key !== 'v') || ev.altKey)
return;
if(!ev.target.matches('input, textarea, select, button'))
$i('umi-msg-text').focus();
msgText.focus();
});
$i('umi-msg-form').addEventListener('submit', ev => {
msgForm.addEventListener('focusin', ev => {
if(mami.settings.get('showMarkupSelector') === 'focus' && Umi.UI.InputMenus.Current() === '')
Umi.UI.InputMenus.Toggle('markup', true);
});
msgForm.addEventListener('focusout', ev => {
if(msgForm.contains(ev.relatedTarget))
return;
if(mami.settings.get('showMarkupSelector') === 'focus' && Umi.UI.InputMenus.Current() === 'markup')
Umi.UI.InputMenus.Toggle('markup', false);
});
msgForm.addEventListener('submit', ev => {
ev.preventDefault();
if(typeof sendMessage !== 'function')
@ -36,13 +51,12 @@ Umi.UI.Hooks = (function() {
}
});
$i('umi-msg-text').addEventListener('input', function(ev) {
const elemInput = $i('umi-msg-text');
const elemParent = elemInput.parentNode;
msgText.addEventListener('input', function(ev) {
const elemParent = msgText.parentNode;
let height = 40;
if(mami.settings.get('expandTextBox') && elemInput.scrollHeight > elemInput.clientHeight)
height = elemInput.scrollHeight;
if(mami.settings.get('expandTextBox') && msgText.scrollHeight > msgText.clientHeight)
height = msgText.scrollHeight;
if(height > 40)
elemParent.style.height = height.toString() + 'px';
@ -50,7 +64,7 @@ Umi.UI.Hooks = (function() {
elemParent.style.height = null;
});
$i('umi-msg-text').addEventListener('keydown', function(ev) {
msgText.addEventListener('keydown', function(ev) {
if(ev.key === 'Tab' && (!ev.shiftKey || !ev.ctrlKey)) {
ev.preventDefault();
@ -94,7 +108,7 @@ Umi.UI.Hooks = (function() {
if((ev.key === 'Enter' || ev.key === 'NumpadEnter') && ev.shiftKey === mami.settings.get('newLineOnEnter')) {
ev.preventDefault();
$i('umi-msg-form').requestSubmit();
msgForm.requestSubmit();
return;
}
});

View file

@ -10,21 +10,30 @@ Umi.UI.InputMenus = (function() {
const createButtonId = id => `umi-msg-menu-btn-${id}`;
const toggle = function(baseId) {
const button = createButtonId(baseId);
const menu = 'umi-msg-menu-sub-' + baseId;
const toggle = function(baseId, force) {
if(typeof force === 'boolean' && (current === baseId) === force)
return;
if($c(inputMenuActive).length)
$c(inputMenuActive)[0].classList.remove(inputMenuActive);
const buttonId = createButtonId(baseId);
const menuId = 'umi-msg-menu-sub-' + baseId;
if($c(inputButtonActive).length)
$c(inputButtonActive)[0].classList.remove(inputButtonActive);
const activeMenus = Array.from($qa(`.${inputMenuActive}`));
if(activeMenus.length > 0)
for(const menu of activeMenus)
menu.classList.remove(inputMenuActive);
const activeButtons = Array.from($qa(`.${inputButtonActive}`));
if(activeButtons.length > 0)
for(const button of activeButtons)
button.classList.remove(inputButtonActive);
if(current !== baseId) {
$i(menu).classList.add(inputMenuActive);
$i(button).classList.add(inputButtonActive);
$i(`umi-msg-menu-sub-${baseId}`)?.classList.add(inputMenuActive);
$i(createButtonId(baseId))?.classList.add(inputButtonActive);
current = baseId;
} else current = '';
} else {
current = '';
}
};
const createButton = function(id, title, onClick) {
@ -43,7 +52,12 @@ Umi.UI.InputMenus = (function() {
};
return {
Current: () => current,
Toggle: toggle,
Add: function(baseId, title, beforeButtonId) {
if(baseId !== 'markup')
throw 'only baseId "markup" may be added';
if(ids.includes(baseId))
return;
ids.push(baseId);
@ -54,9 +68,16 @@ Umi.UI.InputMenus = (function() {
if(!(beforeButton instanceof Element))
beforeButton = $i('umi-msg-send');
$i('umi-msg-container').insertBefore(createButton(baseId, title), beforeButton);
if(typeof title === 'string')
$i('umi-msg-container').insertBefore(createButton(baseId, title), beforeButton);
$i('umi-msg-menu').appendChild(
$e({ attrs: { 'class': ['input__menu', 'input__menu--' + baseId], id: 'umi-msg-menu-sub-' + baseId } })
$e({
attrs: {
'class': ['input__menu', 'input__menu--' + baseId],
id: 'umi-msg-menu-sub-' + baseId,
tabindex: '0',
}
})
);
},
AddButton: function(baseId, title, onClick, beforeButtonId) {

View file

@ -17,23 +17,25 @@ Umi.UI.Markup = (function() {
Umi.UI.View.Focus();
};
const pickerTarget = document.body;
let pickerTarget = document.body;
let picker, pickerVisible = false;
const insert = function(ev) {
if(this.dataset.umiTagName === 'color' && !pickerVisible) {
pickerVisible = true;
if(this.dataset.umiTagName === 'color') {
if(picker === undefined) {
picker = new MamiColourPicker({ presets: futami.get('colours') });
pickerTarget.appendChild(picker.element);
}
if(picker === undefined)
picker = new MamiColourPicker({
presets: futami.get('colours'),
});
pickerTarget.appendChild(picker.element);
picker.dialog(picker.suggestPosition(ev))
.then(colour => insertRaw(`[color=${MamiColour.hex(colour)}]`, '[/color]'))
.finally(() => pickerVisible = false);
if(pickerVisible) {
picker.close();
} else {
pickerVisible = true;
picker.dialog(ev)
.then(colour => insertRaw(`[color=${MamiColour.hex(colour)}]`, '[/color]'))
.catch(() => {}) // noop so the console stops screaming
.finally(() => pickerVisible = false);
}
} else
insertRaw(
this.dataset.umiBeforeCursor,
@ -42,6 +44,7 @@ Umi.UI.Markup = (function() {
};
return {
SetPickerTarget: target => { pickerTarget = target; },
Add: function(name, text, beforeCursor, afterCursor) {
Umi.UI.InputMenus.Get('markup').appendChild($e({
tag: 'button',

View file

@ -240,7 +240,7 @@ Umi.UI.Messages = (function() {
eText.innerText = msgText;
if(!skipTextParsing) {
Umi.UI.Emoticons.Parse(eText, msg);
Umi.UI.Emoticons.Parse(eText, msgAuthor);
Umi.Parsing.Parse(eText, msg);
const textSplit = eText.innerText.split(' ');

View file

@ -1,15 +1,6 @@
#include themes.js
#include utility.js
Umi.UI.View = (function() {
const accentColours = {
'dark': 'Dark',
'light': 'Light',
'blue': 'Blue',
'purple': 'Purple',
'archaic': 'Archaic'
};
const getPosition = function(end) {
return $i('umi-msg-text')[end ? 'selectionEnd' : 'selectionStart'];
};
@ -25,29 +16,6 @@ Umi.UI.View = (function() {
};
return {
AccentColours: accentColours,
AccentReload: function() {
const style = mami.settings.get('style');
if(!accentColours.hasOwnProperty(style))
return;
// should probably be moved elsewhere eventually
// the entire AccentReload function should probably be axed
UmiThemeApply(style);
const chat = $i('umi-chat');
if(chat instanceof Element) {
const compactView = mami.settings.get('compactView');
chat.classList.toggle('chat--compact', compactView);
$i('umi-messages').classList.toggle('chat--compact', compactView);
const forceOldOff = mami.settings.get('tmpDisableOldThemeSys');
for(const name in accentColours)
if(accentColours.hasOwnProperty(name))
chat.classList.toggle(`umi--${name}`, !forceOldOff && name === style);
}
},
Focus: function() {
$i('umi-msg-text').focus();
},
@ -60,8 +28,8 @@ Umi.UI.View = (function() {
return length;
},
EnterAtCursor: function(text, overwrite) {
const value = getText(),
current = getPosition();
const value = getText();
const current = getPosition();
let out = '';
out += value.slice(0, current);