diff options
| -rw-r--r-- | src/features/messages.js | 73 | ||||
| -rw-r--r-- | src/style.css | 7 |
2 files changed, 45 insertions, 35 deletions
diff --git a/src/features/messages.js b/src/features/messages.js index cedd8db..94a5173 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -12535,45 +12535,48 @@ export function messagesFeature(ctx) { signerNotice(), emojiAcStrip(draftKey), h('div', { class: 'chat-compose' }, - onAttach ? h('button', { - class: 'iconbtn chat-clip', title: t('msgAttach'), 'aria-label': t('msgAttach'), - disabled: !!ui.msgUploading, - onClick: () => document.getElementById('msg-file')?.click(), - }, ui.msgUploading ? h('span', { class: 'spinner sm' }) : h('span', { style: 'display:flex', html: CLIP })) : null, onAttach ? h('input', { type: 'file', id: 'msg-file', accept: 'image/*,video/*', style: 'display:none', onChange: (e) => { const f = e.target.files && e.target.files[0]; e.target.value = ''; if (f) onAttach(f); }, }) : null, - h('coinos-text', { - class: 'grow', id: 'msg-draft', placeholder, - value: draftFor(draftKey), - onMedia: onAttach ? (f) => onAttach(f) : null, - onInput: (e) => { setDraft(draftKey, e.target.value); growComposer(e.target); updateEmojiAc(e.target, draftKey); if (onType && e.target.value) onType(); }, - onClick: (e) => updateEmojiAc(e.target, draftKey), // the caret moved - onBlur: () => { if (ui.emojiAc) { ui.emojiAc = null; render(); } }, - onKeydown: (e) => { - const ac = ui.emojiAc && ui.emojiAc.key === draftKey ? ui.emojiAc : null; - if (ac && (e.key === 'Tab' || e.key === 'Enter') && !e.shiftKey) { - const hits = emojiAcHits(ac.q); - if (hits.length) { e.preventDefault(); pickEmojiAc(hits[0]); return; } - } - if (ac && e.key === 'Escape') { e.preventDefault(); ui.emojiAc = null; render(); return; } - if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); onSend(); return; } - if (e.ctrlKey && (e.key === 'j' || e.key === 'J')) { - e.preventDefault(); - const el = e.target; - const { selectionStart: s0, selectionEnd: s1, value } = el; - el.value = value.slice(0, s0) + '\n' + value.slice(s1); - el.selectionStart = el.selectionEnd = s0 + 1; - setDraft(draftKey, el.value); - growComposer(el); - } - }, - // Focus summons the keyboard; some browsers resize only after its - // animation, so re-stick once now and once when it has settled. - onFocus: () => { stickToBottom(); setTimeout(stickToBottom, 350); }, - }), - gifs.button('chat:' + draftKey, 'iconbtn chat-gif'), + // attach + GIF sit inside the field, as in the post composer + h('div', { class: 'compose-box chat-box' }, + h('coinos-text', { + class: 'grow', id: 'msg-draft', placeholder, + value: draftFor(draftKey), + onMedia: onAttach ? (f) => onAttach(f) : null, + onInput: (e) => { setDraft(draftKey, e.target.value); growComposer(e.target); updateEmojiAc(e.target, draftKey); if (onType && e.target.value) onType(); }, + onClick: (e) => updateEmojiAc(e.target, draftKey), // the caret moved + onBlur: () => { if (ui.emojiAc) { ui.emojiAc = null; render(); } }, + onKeydown: (e) => { + const ac = ui.emojiAc && ui.emojiAc.key === draftKey ? ui.emojiAc : null; + if (ac && (e.key === 'Tab' || e.key === 'Enter') && !e.shiftKey) { + const hits = emojiAcHits(ac.q); + if (hits.length) { e.preventDefault(); pickEmojiAc(hits[0]); return; } + } + if (ac && e.key === 'Escape') { e.preventDefault(); ui.emojiAc = null; render(); return; } + if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); onSend(); return; } + if (e.ctrlKey && (e.key === 'j' || e.key === 'J')) { + e.preventDefault(); + const el = e.target; + const { selectionStart: s0, selectionEnd: s1, value } = el; + el.value = value.slice(0, s0) + '\n' + value.slice(s1); + el.selectionStart = el.selectionEnd = s0 + 1; + setDraft(draftKey, el.value); + growComposer(el); + } + }, + // Focus summons the keyboard; some browsers resize only after its + // animation, so re-stick once now and once when it has settled. + onFocus: () => { stickToBottom(); setTimeout(stickToBottom, 350); }, + }), + h('div', { class: 'compose-tools' }, + onAttach ? h('button', { + type: 'button', class: 'compose-tool chat-clip', title: t('msgAttach'), 'aria-label': t('msgAttach'), + disabled: !!ui.msgUploading, + onClick: () => document.getElementById('msg-file')?.click(), + }, ui.msgUploading ? h('span', { class: 'spinner sm' }) : h('span', { style: 'display:flex', html: CLIP })) : null, + gifs.button('chat:' + draftKey, 'compose-tool gif-btn chat-gif'))), h('button', { class: 'btn-primary btn-sm', onClick: onSend }, t('msgSend'))), // a picked GIF goes out at once, after whatever was typed gifs.sheet('chat:' + draftKey, (g) => { diff --git a/src/style.css b/src/style.css index 59ead7b..5ce0211 100644 --- a/src/style.css +++ b/src/style.css @@ -1661,3 +1661,10 @@ body::after { border: 0; border-radius: 8px; background: transparent; color: var(--muted); cursor: pointer; } .compose-tool:hover:not(:disabled) { background: var(--line); color: var(--ink); } .compose-tool.gif-btn { font-size: 11px; width: 38px; } +/* the chat composer: the same tools inside its field, at the right of its + last line — the field keeps room on that side instead of underneath */ +.chat-compose .chat-box { flex: 1; min-width: 0; display: flex; } +.chat-compose .chat-box > coinos-text { flex: 1; padding-bottom: 10px; padding-right: 84px; } +.chat-compose .chat-box > coinos-text:has(+ .compose-tools > :only-child) { padding-right: 48px; } +.chat-compose .chat-box .compose-tools { bottom: 6px; right: 5px; } +.compose-tool.chat-clip { width: 34px; } |
