Compare commits
7 commits
3949d8daed
...
74f116bfac
Author | SHA1 | Date | |
---|---|---|---|
74f116bfac | |||
b0ad0108f8 | |||
cfeaae19a7 | |||
8e162e4163 | |||
81c06d4289 | |||
5dc701cca4 | |||
48a98e7448 |
21 changed files with 484 additions and 236 deletions
|
@ -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
103
src/mami.css/emopick.css
Normal 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;
|
||||
}
|
|
@ -89,6 +89,7 @@ a:hover {
|
|||
@include youare.css;
|
||||
|
||||
@include colpick.css;
|
||||
@include emopick.css;
|
||||
|
||||
@include themes/archaic.css;
|
||||
@include themes/blue.css;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
146
src/mami.js/emotes/picker.jsx
Normal file
146
src/mami.js/emotes/picker.jsx
Normal 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; });
|
||||
},
|
||||
};
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
},
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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(' ');
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue