Created a web component registry and exports components as modules in order to facilitate their customization
This commit is contained in:
parent
3607bb6dc8
commit
82357f7d97
|
@ -122,7 +122,7 @@ async function fetchCommandForm (command) {
|
|||
}
|
||||
|
||||
|
||||
export class AdHocCommands extends CustomElement {
|
||||
export default class AdHocCommands extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -239,4 +239,4 @@ export class AdHocCommands extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
window.customElements.define('converse-adhoc-commands', AdHocCommands);
|
||||
api.elements.define('converse-adhoc-commands', AdHocCommands);
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { AutoComplete, FILTER_CONTAINS, FILTER_STARTSWITH } from "../converse-autocomplete.js";
|
||||
import { CustomElement } from './element.js';
|
||||
import { html } from 'lit-element';
|
||||
import { api } from "@converse/headless/converse-core";
|
||||
|
||||
|
||||
export class AutoCompleteComponent extends CustomElement {
|
||||
export default class AutoCompleteComponent extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -70,4 +70,4 @@ export class AutoCompleteComponent extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
window.customElements.define('converse-autocomplete', AutoCompleteComponent);
|
||||
api.elements.define('converse-autocomplete', AutoCompleteComponent);
|
||||
|
|
|
@ -3,9 +3,9 @@ import xss from "xss/dist/xss";
|
|||
import { CustomElement } from './element.js';
|
||||
import { html } from 'lit-element';
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
import { api } from "@converse/headless/converse-core";
|
||||
|
||||
|
||||
class ChatContent extends CustomElement {
|
||||
export default class ChatContent extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -27,4 +27,4 @@ class ChatContent extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-chat-content', ChatContent);
|
||||
api.elements.define('converse-chat-content', ChatContent);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import DOMNavigator from "../dom-navigator";
|
||||
import { CustomElement } from './element.js';
|
||||
import { converse } from "@converse/headless/converse-core";
|
||||
import { converse, api } from "@converse/headless/converse-core";
|
||||
import { html } from 'lit-element';
|
||||
import { until } from 'lit-html/directives/until.js';
|
||||
|
||||
|
@ -48,7 +48,7 @@ export class BaseDropdown extends CustomElement {
|
|||
}
|
||||
|
||||
|
||||
export class DropdownList extends BaseDropdown {
|
||||
export default class DropdownList extends BaseDropdown {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -109,4 +109,4 @@ export class DropdownList extends BaseDropdown {
|
|||
}
|
||||
}
|
||||
|
||||
window.customElements.define('converse-dropdown', DropdownList);
|
||||
api.elements.define('converse-dropdown', DropdownList);
|
||||
|
|
107
src/components/emoji-picker-content.js
Normal file
107
src/components/emoji-picker-content.js
Normal file
|
@ -0,0 +1,107 @@
|
|||
import sizzle from 'sizzle';
|
||||
import { CustomElement } from './element.js';
|
||||
import { _converse, api } from "@converse/headless/converse-core";
|
||||
import { html } from "lit-element";
|
||||
import { tpl_all_emojis, tpl_search_results } from "../templates/emoji_picker.js";
|
||||
|
||||
|
||||
export default class EmojiPickerContent extends CustomElement {
|
||||
static get properties () {
|
||||
return {
|
||||
'chatview': { type: Object },
|
||||
'search_results': { type: Array },
|
||||
'current_skintone': { type: String },
|
||||
'model': { type: Object },
|
||||
'query': { type: String },
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const props = {
|
||||
'current_skintone': this.current_skintone,
|
||||
'insertEmoji': ev => this.insertEmoji(ev),
|
||||
'query': this.query,
|
||||
'search_results': this.search_results,
|
||||
'shouldBeHidden': shortname => this.shouldBeHidden(shortname),
|
||||
}
|
||||
return html`
|
||||
<div class="emoji-picker__lists">
|
||||
${tpl_search_results(props)}
|
||||
${tpl_all_emojis(props)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
this.initIntersectionObserver();
|
||||
}
|
||||
|
||||
initIntersectionObserver () {
|
||||
if (!window.IntersectionObserver) {
|
||||
return;
|
||||
}
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
} else {
|
||||
const options = {
|
||||
root: this.querySelector('.emoji-picker__lists'),
|
||||
threshold: [0.1]
|
||||
}
|
||||
const handler = ev => this.setCategoryOnVisibilityChange(ev);
|
||||
this.observer = new IntersectionObserver(handler, options);
|
||||
}
|
||||
sizzle('.emoji-picker', this).forEach(a => this.observer.observe(a));
|
||||
}
|
||||
|
||||
setCategoryOnVisibilityChange (ev) {
|
||||
const selected = this.parentElement.navigator.selected;
|
||||
const intersection_with_selected = ev.filter(i => i.target.contains(selected)).pop();
|
||||
let current;
|
||||
// Choose the intersection that contains the currently selected
|
||||
// element, or otherwise the one with the largest ratio.
|
||||
if (intersection_with_selected) {
|
||||
current = intersection_with_selected;
|
||||
} else {
|
||||
current = ev.reduce((p, c) => c.intersectionRatio >= (p?.intersectionRatio || 0) ? c : p, null);
|
||||
}
|
||||
if (current && current.isIntersecting) {
|
||||
const category = current.target.getAttribute('data-category');
|
||||
if (category !== this.model.get('current_category')) {
|
||||
this.parentElement.preserve_scroll = true;
|
||||
this.model.save({'current_category': category});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insertEmoji (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target;
|
||||
const replace = this.model.get('autocompleting');
|
||||
const position = this.model.get('position');
|
||||
this.model.set({'autocompleting': null, 'position': null, 'query': ''});
|
||||
this.chatview.insertIntoTextArea(target.getAttribute('data-emoji'), replace, false, position);
|
||||
this.chatview.emoji_dropdown.toggle();
|
||||
}
|
||||
|
||||
shouldBeHidden (shortname) {
|
||||
// Helper method for the template which decides whether an
|
||||
// emoji should be hidden, based on which skin tone is
|
||||
// currently being applied.
|
||||
if (shortname.includes('_tone')) {
|
||||
if (!this.current_skintone || !shortname.includes(this.current_skintone)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (this.current_skintone && _converse.emojis.toned.includes(shortname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.query && !_converse.FILTER_CONTAINS(shortname, this.query)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
api.elements.define('converse-emoji-picker-content', EmojiPickerContent);
|
|
@ -1,115 +1,14 @@
|
|||
import "./emoji-picker-content.js";
|
||||
import DOMNavigator from "../dom-navigator";
|
||||
import sizzle from 'sizzle';
|
||||
import { CustomElement } from './element.js';
|
||||
import { _converse, converse } from "@converse/headless/converse-core";
|
||||
import { _converse, api, converse } from "@converse/headless/converse-core";
|
||||
import { debounce, find } from "lodash-es";
|
||||
import { html } from "lit-element";
|
||||
import { tpl_all_emojis, tpl_emoji_picker, tpl_search_results } from "../templates/emoji_picker.js";
|
||||
import { tpl_emoji_picker } from "../templates/emoji_picker.js";
|
||||
|
||||
const u = converse.env.utils;
|
||||
|
||||
|
||||
export class EmojiPickerContent extends CustomElement {
|
||||
static get properties () {
|
||||
return {
|
||||
'chatview': { type: Object },
|
||||
'search_results': { type: Array },
|
||||
'current_skintone': { type: String },
|
||||
'model': { type: Object },
|
||||
'query': { type: String },
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const props = {
|
||||
'current_skintone': this.current_skintone,
|
||||
'insertEmoji': ev => this.insertEmoji(ev),
|
||||
'query': this.query,
|
||||
'search_results': this.search_results,
|
||||
'shouldBeHidden': shortname => this.shouldBeHidden(shortname),
|
||||
}
|
||||
return html`
|
||||
<div class="emoji-picker__lists">
|
||||
${tpl_search_results(props)}
|
||||
${tpl_all_emojis(props)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
this.initIntersectionObserver();
|
||||
}
|
||||
|
||||
initIntersectionObserver () {
|
||||
if (!window.IntersectionObserver) {
|
||||
return;
|
||||
}
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
} else {
|
||||
const options = {
|
||||
root: this.querySelector('.emoji-picker__lists'),
|
||||
threshold: [0.1]
|
||||
}
|
||||
const handler = ev => this.setCategoryOnVisibilityChange(ev);
|
||||
this.observer = new IntersectionObserver(handler, options);
|
||||
}
|
||||
sizzle('.emoji-picker', this).forEach(a => this.observer.observe(a));
|
||||
}
|
||||
|
||||
setCategoryOnVisibilityChange (ev) {
|
||||
const selected = this.parentElement.navigator.selected;
|
||||
const intersection_with_selected = ev.filter(i => i.target.contains(selected)).pop();
|
||||
let current;
|
||||
// Choose the intersection that contains the currently selected
|
||||
// element, or otherwise the one with the largest ratio.
|
||||
if (intersection_with_selected) {
|
||||
current = intersection_with_selected;
|
||||
} else {
|
||||
current = ev.reduce((p, c) => c.intersectionRatio >= (p?.intersectionRatio || 0) ? c : p, null);
|
||||
}
|
||||
if (current && current.isIntersecting) {
|
||||
const category = current.target.getAttribute('data-category');
|
||||
if (category !== this.model.get('current_category')) {
|
||||
this.parentElement.preserve_scroll = true;
|
||||
this.model.save({'current_category': category});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insertEmoji (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const target = ev.target.nodeName === 'IMG' ? ev.target.parentElement : ev.target;
|
||||
const replace = this.model.get('autocompleting');
|
||||
const position = this.model.get('position');
|
||||
this.model.set({'autocompleting': null, 'position': null, 'query': ''});
|
||||
this.chatview.insertIntoTextArea(target.getAttribute('data-emoji'), replace, false, position);
|
||||
this.chatview.emoji_dropdown.toggle();
|
||||
}
|
||||
|
||||
shouldBeHidden (shortname) {
|
||||
// Helper method for the template which decides whether an
|
||||
// emoji should be hidden, based on which skin tone is
|
||||
// currently being applied.
|
||||
if (shortname.includes('_tone')) {
|
||||
if (!this.current_skintone || !shortname.includes(this.current_skintone)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (this.current_skintone && _converse.emojis.toned.includes(shortname)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.query && !_converse.FILTER_CONTAINS(shortname, this.query)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class EmojiPicker extends CustomElement {
|
||||
export default class EmojiPicker extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -343,5 +242,4 @@ export class EmojiPicker extends CustomElement {
|
|||
}
|
||||
|
||||
|
||||
window.customElements.define('converse-emoji-picker', EmojiPicker);
|
||||
window.customElements.define('converse-emoji-picker-content', EmojiPickerContent);
|
||||
api.elements.define('converse-emoji-picker', EmojiPicker);
|
||||
|
|
|
@ -6,7 +6,7 @@ import { html } from 'lit-element';
|
|||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
|
||||
|
||||
class ChatHelp extends CustomElement {
|
||||
export default class ChatHelp extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -42,4 +42,4 @@ class ChatHelp extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-chat-help', ChatHelp);
|
||||
api.elements.define('converse-chat-help', ChatHelp);
|
||||
|
|
|
@ -2,11 +2,12 @@ import { CustomElement } from './element.js';
|
|||
import { __ } from '@converse/headless/i18n';
|
||||
import { html } from 'lit-element';
|
||||
import { renderAvatar } from "../templates/directives/avatar.js";
|
||||
import { api } from "@converse/headless/converse-core";
|
||||
|
||||
const i18n_alt_avatar = __('Your avatar image');
|
||||
|
||||
|
||||
export class ImagePicker extends CustomElement {
|
||||
export default class ImagePicker extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -43,4 +44,4 @@ export class ImagePicker extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
window.customElements.define('converse-image-picker', ImagePicker);
|
||||
api.elements.define('converse-image-picker', ImagePicker);
|
||||
|
|
|
@ -75,4 +75,4 @@ class MessageActions extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-message-actions', MessageActions);
|
||||
api.elements.define('converse-message-actions', MessageActions);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { CustomElement } from './element.js';
|
||||
import { renderBodyText } from './../templates/directives/body';
|
||||
import { api } from "@converse/headless/converse-core";
|
||||
|
||||
|
||||
class MessageBody extends CustomElement {
|
||||
export default class MessageBody extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -17,4 +17,4 @@ class MessageBody extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-chat-message-body', MessageBody);
|
||||
api.elements.define('converse-chat-message-body', MessageBody);
|
||||
|
|
|
@ -81,7 +81,7 @@ function getHats (model) {
|
|||
}
|
||||
|
||||
|
||||
class MessageHistory extends CustomElement {
|
||||
export default class MessageHistory extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -122,4 +122,4 @@ class MessageHistory extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-message-history', MessageHistory);
|
||||
api.elements.define('converse-message-history', MessageHistory);
|
||||
|
|
|
@ -21,7 +21,7 @@ const i18n_show_less = __('Show less');
|
|||
const i18n_uploading = __('Uploading file:');
|
||||
|
||||
|
||||
class Message extends CustomElement {
|
||||
export default class Message extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
|
@ -265,4 +265,4 @@ class Message extends CustomElement {
|
|||
}
|
||||
}
|
||||
|
||||
customElements.define('converse-chat-message', Message);
|
||||
api.elements.define('converse-chat-message', Message);
|
||||
|
|
|
@ -49,6 +49,8 @@ converse.plugins.add('converse-chatboxviews', {
|
|||
* loaded by converse.js's plugin machinery.
|
||||
*/
|
||||
|
||||
api.elements.register();
|
||||
|
||||
api.promises.add(['chatBoxViewsInitialized']);
|
||||
|
||||
// Configuration values for this plugin
|
||||
|
|
19
src/converse-registry.js
Normal file
19
src/converse-registry.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { _converse } from "@converse/headless/converse-core";
|
||||
|
||||
const registry = {};
|
||||
|
||||
function define (componentName, componentClass) {
|
||||
this.registry[componentName] = componentClass;
|
||||
}
|
||||
|
||||
function register () {
|
||||
Object.keys(registry).map(componentName =>
|
||||
window.customElements.define(componentName, registry[componentName])
|
||||
);
|
||||
}
|
||||
|
||||
_converse.api.elements = {
|
||||
registry,
|
||||
define,
|
||||
register
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
* Any of the following components may be removed if they're not needed.
|
||||
*/
|
||||
import "@converse/headless/headless";
|
||||
import "converse-registry";
|
||||
import "converse-autocomplete";
|
||||
import "converse-bookmark-views"; // Views for XEP-0048 Bookmarks
|
||||
import "converse-chatview"; // Renders standalone chat boxes for single user chat
|
||||
|
|
Loading…
Reference in New Issue
Block a user