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-picker-content.js";
|
||||||
|
import './emoji-dropdown.js';
|
||||||
import DOMNavigator from "shared/dom-navigator";
|
import DOMNavigator from "shared/dom-navigator";
|
||||||
import debounce from 'lodash-es/debounce';
|
import debounce from 'lodash-es/debounce';
|
||||||
import { BaseDropdown } from "shared/components/dropdown.js";
|
|
||||||
import { CustomElement } from 'shared/components/element.js';
|
import { CustomElement } from 'shared/components/element.js';
|
||||||
import { __ } from 'i18n';
|
|
||||||
import { _converse, api, converse } from "@converse/headless/core";
|
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 { tpl_emoji_picker } from "./templates/emoji-picker.js";
|
||||||
import { until } from 'lit/directives/until.js';
|
|
||||||
|
|
||||||
import './styles/emoji.scss';
|
import './styles/emoji.scss';
|
||||||
|
|
||||||
@ -30,6 +26,7 @@ export default class EmojiPicker extends CustomElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
firstUpdated () {
|
firstUpdated () {
|
||||||
|
super.firstUpdated();
|
||||||
this.listenTo(this.model, 'change', o => this.onModelChanged(o.changed));
|
this.listenTo(this.model, 'change', o => this.onModelChanged(o.changed));
|
||||||
this.initArrowNavigation();
|
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);
|
api.elements.define('converse-emoji-picker', EmojiPicker);
|
||||||
|
@ -1,76 +1,11 @@
|
|||||||
import DOMNavigator from "shared/dom-navigator.js";
|
import DOMNavigator from "shared/dom-navigator.js";
|
||||||
import { CustomElement } from './element.js';
|
|
||||||
import { converse, api } from "@converse/headless/core";
|
import { converse, api } from "@converse/headless/core";
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { until } from 'lit/directives/until.js';
|
import { until } from 'lit/directives/until.js';
|
||||||
|
import DropdownBase from 'shared/components/dropdownbase.js';
|
||||||
const u = converse.env.utils;
|
|
||||||
|
|
||||||
|
|
||||||
export class BaseDropdown extends CustomElement {
|
export default class Dropdown extends DropdownBase {
|
||||||
|
|
||||||
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 {
|
|
||||||
|
|
||||||
static get properties () {
|
static get properties () {
|
||||||
return {
|
return {
|
||||||
@ -93,9 +28,14 @@ export default class DropdownList extends BaseDropdown {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
firstUpdated () {
|
||||||
|
super.firstUpdated();
|
||||||
|
this.initArrowNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
hideMenu () {
|
hideMenu () {
|
||||||
super.hideMenu();
|
super.hideMenu();
|
||||||
this.navigator.disable();
|
this.navigator?.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
initArrowNavigation () {
|
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