reloadList() {
this.stopEdit();
- this.mainTabs.components.tabs.show('items');
+ /** @var {Tabs} */
+ const tabs = window.$components.firstOnElement(this.mainTabs, 'tabs');
+ tabs.show('items');
window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
this.list.innerHTML = resp.data;
window.$components.init(this.list);
}
this.loadHistory();
- this.popup.components.popup.show(() => {
+ this.getPopup().show(() => {
Code.updateLayout(this.editor);
this.editor.focus();
}, () => {
}
hide() {
- this.popup.components.popup.hide();
+ this.getPopup().hide();
this.addHistory();
}
+ /**
+ * @returns {Popup}
+ */
+ getPopup() {
+ return window.$components.firstOnElement(this.popup, 'popup');
+ }
+
async updateEditorMode(language) {
const Code = await window.importVersioned('code');
Code.setMode(this.editor, language, this.editor.getValue());
* @returns {Popup}
*/
getPopup() {
- return this.container.components.popup;
+ return window.$components.firstOnElement(this.container, 'popup');
}
/**
show(callback) {
this.callback = callback;
- this.container.components.popup.show();
+ this.getPopup().show();
this.getSelector().focusSearch();
}
hide() {
- this.container.components.popup.hide();
+ this.getPopup().hide();
}
+ /**
+ * @returns {Popup}
+ */
+ getPopup() {
+ return window.$components.firstOnElement(this.container, 'popup');
+ }
+
+ /**
+ * @returns {EntitySelector}
+ */
getSelector() {
- return this.selectorEl.components['entity-selector'];
+ return window.$components.firstOnElement(this.selectorEl, 'entity-selector');
}
onSelectButtonClick() {
this.callback = callback;
this.type = type;
- this.popupEl.components.popup.show();
+ this.getPopup().show();
this.dropzoneContainer.classList.toggle('hidden', type !== 'gallery');
if (!this.hasData) {
}
hide() {
- this.popupEl.components.popup.hide();
+ this.getPopup().hide();
+ }
+
+ /**
+ * @returns {Popup}
+ */
+ getPopup() {
+ return window.$components.firstOnElement(this.popupEl, 'popup');
}
async loadGallery() {
event.preventDefault();
const link = event.target.closest('a').href;
- const dialog = this.switchDialogContainer.components['confirm-dialog'];
+ /** @var {ConfirmDialog} **/
+ const dialog = window.$components.firstOnElement(this.switchDialogContainer, 'confirm-dialog');
const [saved, confirmed] = await Promise.all([this.saveDraft(), dialog.show()]);
if (saved && confirmed) {
setupListeners() {
this.container.addEventListener('change', event => {
- const addRemoveComponent = this.addRemoveComponentEl.components['add-remove-rows'];
+ /** @var {AddRemoveRows} **/
+ const addRemoveComponent = window.$components.firstOnElement(this.addRemoveComponentEl, 'add-remove-rows');
if (!this.hasEmptyRows()) {
addRemoveComponent.add();
}
export class UserSelect extends Component {
setup() {
+ this.container = this.$el;
this.input = this.$refs.input;
this.userInfoContainer = this.$refs.userInfo;
- this.hide = this.$el.components.dropdown.hide;
-
- onChildEvent(this.$el, 'a.dropdown-search-item', 'click', this.selectUser.bind(this));
+ onChildEvent(this.container, 'a.dropdown-search-item', 'click', this.selectUser.bind(this));
}
selectUser(event, userEl) {
this.hide();
}
+ hide() {
+ /** @var {Dropdown} **/
+ const dropdown = window.$components.firstOnElement(this.container, 'dropdown');
+ dropdown.hide();
+ }
+
}
\ No newline at end of file
+/**
+ * A mapping of active components keyed by name, with values being arrays of component
+ * instances since there can be multiple components of the same type.
+ * @type {Object<String, Component[]>}
+ */
const components = {};
-const componentMap = {};
+
+/**
+ * A mapping of component class models, keyed by name.
+ * @type {Object<String, Constructor<Component>>}
+ */
+const componentModelMap = {};
+
+/**
+ * A mapping of active component maps, keyed by the element components are assigned to.
+ * @type {WeakMap<Element, Object<String, Component>>}
+ */
+const elementComponentMap = new WeakMap();
/**
* Initialize a component instance on the given dom element.
*/
function initComponent(name, element) {
/** @type {Function<Component>|undefined} **/
- const componentModel = componentMap[name];
+ const componentModel = componentModelMap[name];
if (componentModel === undefined) return;
// Create our component instance
}
components[name].push(instance);
- // Add to element listing
- if (typeof element.components === 'undefined') {
- element.components = {};
- }
- element.components[name] = instance;
+ // Add to element mapping
+ const elComponents = elementComponentMap.get(element) || {};
+ elComponents[name] = instance;
+ elementComponentMap.set(element, elComponents);
}
/**
export function register(mapping) {
const keys = Object.keys(mapping);
for (const key of keys) {
- componentMap[camelToKebab(key)] = mapping[key];
+ componentModelMap[camelToKebab(key)] = mapping[key];
}
}
return components[name] || [];
}
+/**
+ * Get the first component, of the given name, that's assigned to the given element.
+ * @param {Element} element
+ * @param {String} name
+ * @returns {Component|null}
+ */
+export function firstOnElement(element, name) {
+ const elComponents = elementComponentMap.get(element) || {};
+ return elComponents[name] || null;
+}
+
function camelToKebab(camelStr) {
return camelStr.replace(/[A-Z]/g, (str, offset) => (offset > 0 ? '-' : '') + str.toLowerCase());
}
\ No newline at end of file