Fixes concerning dropdowns
This commit is contained in:
parent
46f567d0d1
commit
73989e09a9
96
src/shared/chat/emoji-dropdown.js
Normal file
96
src/shared/chat/emoji-dropdown.js
Normal file
@ -0,0 +1,96 @@
|
||||
import DropdownBase from "shared/components/dropdown.js";
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { html } from "lit";
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
import { until } from 'lit/directives/until.js';
|
||||
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
export default class EmojiDropdown extends DropdownBase {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
chatview: { type: Object }
|
||||
};
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
// This is an optimization, we lazily render the emoji picker, otherwise tests slow to a crawl.
|
||||
this.render_emojis = false;
|
||||
}
|
||||
|
||||
initModel () {
|
||||
if (!this.init_promise) {
|
||||
this.init_promise = (async () => {
|
||||
await api.emojis.initialize()
|
||||
const id = `converse.emoji-${_converse.bare_jid}-${this.chatview.model.get('jid')}`;
|
||||
this.model = new _converse.EmojiPicker({'id': id});
|
||||
initStorage(this.model, id);
|
||||
await new Promise(resolve => this.model.fetch({'success': resolve, 'error': resolve}));
|
||||
// We never want still be in the autocompleting state upon page load
|
||||
this.model.set({'autocompleting': null, 'ac_position': null});
|
||||
})();
|
||||
}
|
||||
return this.init_promise;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="dropup">
|
||||
<button class="toggle-emojis"
|
||||
title="${__('Insert emojis')}"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<converse-icon
|
||||
class="fa fa-smile "
|
||||
path-prefix="${api.settings.get('assets_path')}"
|
||||
size="1em"></converse-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
${until(this.initModel().then(() => html`
|
||||
<converse-emoji-picker
|
||||
.chatview=${this.chatview}
|
||||
.model=${this.model}
|
||||
?render_emojis=${this.render_emojis}
|
||||
current_category="${this.model.get('current_category') || ''}"
|
||||
current_skintone="${this.model.get('current_skintone') || ''}"
|
||||
query="${this.model.get('query') || ''}"
|
||||
></converse-emoji-picker>`), '')}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
super.connectedCallback();
|
||||
this.render_emojis = false;
|
||||
}
|
||||
|
||||
toggleMenu (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
if (u.hasClass('show', this.menu)) {
|
||||
if (u.ancestor(ev.target, '.toggle-emojis')) {
|
||||
this.hideMenu();
|
||||
}
|
||||
} else {
|
||||
this.showMenu();
|
||||
}
|
||||
}
|
||||
|
||||
async showMenu () {
|
||||
await this.initModel();
|
||||
if (!this.render_emojis) {
|
||||
// Trigger an update so that emojis are rendered
|
||||
this.render_emojis = true;
|
||||
await this.requestUpdate();
|
||||
}
|
||||
super.showMenu();
|
||||
setTimeout(() => this.querySelector('.emoji-search')?.focus());
|
||||
}
|
||||
}
|
||||
|
||||
api.elements.define('converse-emoji-dropdown', EmojiDropdown);
|
@ -1,14 +1,10 @@
|
||||
import "./emoji-picker-content.js";
|
||||
import './emoji-dropdown.js';
|
||||
import DOMNavigator from "shared/dom-navigator";
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import { BaseDropdown } from "shared/components/dropdown.js";
|
||||
import { CustomElement } from 'shared/components/element.js';
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { html } from "lit";
|
||||
import { initStorage } from '@converse/headless/shared/utils.js';
|
||||
import { tpl_emoji_picker } from "./templates/emoji-picker.js";
|
||||
import { until } from 'lit/directives/until.js';
|
||||
|
||||
import './styles/emoji.scss';
|
||||
|
||||
@ -30,6 +26,7 @@ export default class EmojiPicker extends CustomElement {
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
super.firstUpdated();
|
||||
this.listenTo(this.model, 'change', o => this.onModelChanged(o.changed));
|
||||
this.initArrowNavigation();
|
||||
}
|
||||
@ -275,91 +272,4 @@ export default class EmojiPicker extends CustomElement {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class EmojiDropdown extends BaseDropdown {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
chatview: { type: Object }
|
||||
};
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super();
|
||||
// This is an optimization, we lazily render the emoji picker, otherwise tests slow to a crawl.
|
||||
this.render_emojis = false;
|
||||
}
|
||||
|
||||
initModel () {
|
||||
if (!this.init_promise) {
|
||||
this.init_promise = (async () => {
|
||||
await api.emojis.initialize()
|
||||
const id = `converse.emoji-${_converse.bare_jid}-${this.chatview.model.get('jid')}`;
|
||||
this.model = new _converse.EmojiPicker({'id': id});
|
||||
initStorage(this.model, id);
|
||||
await new Promise(resolve => this.model.fetch({'success': resolve, 'error': resolve}));
|
||||
// We never want still be in the autocompleting state upon page load
|
||||
this.model.set({'autocompleting': null, 'ac_position': null});
|
||||
})();
|
||||
}
|
||||
return this.init_promise;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="dropup">
|
||||
<button class="toggle-emojis"
|
||||
title="${__('Insert emojis')}"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false">
|
||||
<converse-icon
|
||||
class="fa fa-smile "
|
||||
path-prefix="${api.settings.get('assets_path')}"
|
||||
size="1em"></converse-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
${until(this.initModel().then(() => html`
|
||||
<converse-emoji-picker
|
||||
.chatview=${this.chatview}
|
||||
.model=${this.model}
|
||||
?render_emojis=${this.render_emojis}
|
||||
current_category="${this.model.get('current_category') || ''}"
|
||||
current_skintone="${this.model.get('current_skintone') || ''}"
|
||||
query="${this.model.get('query') || ''}"
|
||||
></converse-emoji-picker>`), '')}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
super.connectedCallback();
|
||||
this.render_emojis = false;
|
||||
}
|
||||
|
||||
toggleMenu (ev) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
if (u.hasClass('show', this.menu)) {
|
||||
if (u.ancestor(ev.target, '.toggle-emojis')) {
|
||||
this.hideMenu();
|
||||
}
|
||||
} else {
|
||||
this.showMenu();
|
||||
}
|
||||
}
|
||||
|
||||
async showMenu () {
|
||||
await this.initModel();
|
||||
if (!this.render_emojis) {
|
||||
// Trigger an update so that emojis are rendered
|
||||
this.render_emojis = true;
|
||||
await this.requestUpdate();
|
||||
}
|
||||
super.showMenu();
|
||||
setTimeout(() => this.querySelector('.emoji-search')?.focus());
|
||||
}
|
||||
}
|
||||
|
||||
api.elements.define('converse-emoji-dropdown', EmojiDropdown);
|
||||
api.elements.define('converse-emoji-picker', EmojiPicker);
|
||||
|
@ -1,76 +1,11 @@
|
||||
import DOMNavigator from "shared/dom-navigator.js";
|
||||
import { CustomElement } from './element.js';
|
||||
import { converse, api } from "@converse/headless/core";
|
||||
import { html } from 'lit';
|
||||
import { until } from 'lit/directives/until.js';
|
||||
|
||||
const u = converse.env.utils;
|
||||
import DropdownBase from 'shared/components/dropdownbase.js';
|
||||
|
||||
|
||||
export class BaseDropdown extends CustomElement {
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.registerEvents();
|
||||
}
|
||||
|
||||
registerEvents() {
|
||||
this.clickOutside = this._clickOutside.bind(this);
|
||||
document.addEventListener('click', this.clickOutside);
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
super.firstUpdated();
|
||||
this.initArrowNavigation();
|
||||
this.menu = this.querySelector('.dropdown-menu');
|
||||
this.dropdown = this.firstElementChild;
|
||||
this.button = this.dropdown.querySelector('button');
|
||||
this.dropdown.addEventListener('click', ev => this.toggleMenu(ev));
|
||||
this.dropdown.addEventListener('keyup', ev => this.handleKeyUp(ev));
|
||||
}
|
||||
|
||||
_clickOutside(ev) {
|
||||
if (!this.contains(ev.composedPath()[0])) {
|
||||
this.hideMenu(ev);
|
||||
}
|
||||
}
|
||||
|
||||
hideMenu () {
|
||||
u.removeClass('show', this.menu);
|
||||
this.button?.setAttribute('aria-expanded', false);
|
||||
this.button?.blur();
|
||||
}
|
||||
|
||||
showMenu () {
|
||||
u.addClass('show', this.menu);
|
||||
this.button.setAttribute('aria-expanded', true);
|
||||
}
|
||||
|
||||
toggleMenu (ev) {
|
||||
ev.preventDefault();
|
||||
if (u.hasClass('show', this.menu)) {
|
||||
this.hideMenu();
|
||||
} else {
|
||||
this.showMenu();
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyUp (ev) {
|
||||
if (ev.keyCode === converse.keycodes.ESCAPE) {
|
||||
this.hideMenu();
|
||||
} else if (ev.keyCode === converse.keycodes.DOWN_ARROW && this.navigator && !this.navigator.enabled) {
|
||||
this.enableArrowNavigation(ev);
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback () {
|
||||
document.removeEventListener('click', this.clickOutside);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default class DropdownList extends BaseDropdown {
|
||||
export default class Dropdown extends DropdownBase {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
@ -93,9 +28,14 @@ export default class DropdownList extends BaseDropdown {
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
super.firstUpdated();
|
||||
this.initArrowNavigation();
|
||||
}
|
||||
|
||||
hideMenu () {
|
||||
super.hideMenu();
|
||||
this.navigator.disable();
|
||||
this.navigator?.disable();
|
||||
}
|
||||
|
||||
initArrowNavigation () {
|
||||
@ -125,4 +65,4 @@ export default class DropdownList extends BaseDropdown {
|
||||
}
|
||||
}
|
||||
|
||||
api.elements.define('converse-dropdown', DropdownList);
|
||||
api.elements.define('converse-dropdown', Dropdown);
|
||||
|
64
src/shared/components/dropdownbase.js
Normal file
64
src/shared/components/dropdownbase.js
Normal file
@ -0,0 +1,64 @@
|
||||
import { CustomElement } from './element.js';
|
||||
import { converse } from "@converse/headless/core";
|
||||
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
export default class DropdownBase extends CustomElement {
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.registerEvents();
|
||||
}
|
||||
|
||||
registerEvents() {
|
||||
this.clickOutside = (ev) => this._clickOutside(ev);
|
||||
document.addEventListener('click', this.clickOutside);
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
super.firstUpdated();
|
||||
this.menu = this.querySelector('.dropdown-menu');
|
||||
this.dropdown = this.firstElementChild;
|
||||
this.button = this.dropdown.querySelector('button');
|
||||
this.dropdown.addEventListener('click', ev => this.toggleMenu(ev));
|
||||
this.dropdown.addEventListener('keyup', ev => this.handleKeyUp(ev));
|
||||
}
|
||||
|
||||
_clickOutside(ev) {
|
||||
if (!this.contains(ev.composedPath()[0])) {
|
||||
this.hideMenu(ev);
|
||||
}
|
||||
}
|
||||
|
||||
hideMenu () {
|
||||
u.removeClass('show', this.menu);
|
||||
this.button?.setAttribute('aria-expanded', false);
|
||||
this.button?.blur();
|
||||
}
|
||||
|
||||
showMenu () {
|
||||
u.addClass('show', this.menu);
|
||||
this.button.setAttribute('aria-expanded', true);
|
||||
}
|
||||
|
||||
toggleMenu (ev) {
|
||||
ev.preventDefault();
|
||||
if (u.hasClass('show', this.menu)) {
|
||||
this.hideMenu();
|
||||
} else {
|
||||
this.showMenu();
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyUp (ev) {
|
||||
if (ev.keyCode === converse.keycodes.ESCAPE) {
|
||||
this.hideMenu();
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback () {
|
||||
document.removeEventListener('click', this.clickOutside);
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user