@dbp-toolkit/common
Version:
You can provide attributes (e.g. `global-name`) for components inside the provider:
182 lines (160 loc) • 5.99 kB
JavaScript
export class ProviderAdapter extends HTMLElement {
constructor() {
super();
this.connected = false;
this.deferSubscribe = false;
this.deferUnSubscribe = false;
// attributes (if they exist) will be updated if a property is changed by "subscribe"
this.reflectAttribute = true;
// default values
this.subscribe = '';
this.unsubscribe = '';
}
getPropertyByAttributeName(name) {
return this[this.findPropertyName(name)];
}
setPropertyByAttributeName(name, value) {
this[this.findPropertyName(name)] = value;
}
connectedCallback() {
this.connected = true;
// get all *not observed* attributes
if (this.hasAttributes()) {
const attrs = this.attributes;
for (let i = attrs.length - 1; i >= 0; i--) {
if (['id', 'class', 'style', 'data-tag-name'].includes(attrs[i].name)) {
continue;
}
this.setPropertiesToChildNodes();
this.attributeChangedCallback(
attrs[i].name,
this.getPropertyByAttributeName(attrs[i].name),
attrs[i].value,
);
}
}
}
disconnectedCallback() {
const attrs = this.subscribe.split(',');
attrs.forEach((element) => this.unSubscribeProviderFor(element));
}
subscribeProviderFor(element) {
const pair = element.trim().split(':');
const local = pair[0];
const global = pair[1] || local;
const that = this;
const event = new CustomEvent('dbp-subscribe', {
bubbles: true,
composed: true,
detail: {
name: global,
callback: (value) => {
that.setPropertiesToChildNodes(local, value);
// If value is an object set it directly as property
if (typeof value === 'object' && value !== null) {
that.setPropertyByAttributeName(local, value);
} else {
that.attributeChangedCallback(
local,
that.getPropertyByAttributeName(local),
value,
);
// check if an attribute also exists in the tag
if (that.getAttribute(local) !== null) {
// we don't support attributes and provider values at the same time
console.warn(
'Provider callback: "' +
local +
'" is also an attribute in tag "' +
that.tagName +
'", this is not supported!',
);
// update attribute if reflectAttribute is enabled
if (that.reflectAttribute) {
that.setAttribute(local, value);
}
}
}
},
sender: this,
},
});
this.dispatchEvent(event);
}
unSubscribeProviderFor(element) {
const pair = element.trim().split(':');
const global = pair[1] || pair[0];
const event = new CustomEvent('dbp-unsubscribe', {
bubbles: true,
composed: true,
detail: {
name: global,
sender: this,
},
});
this.dispatchEvent(event);
}
static get properties() {
return {
subscribe: {type: String},
unsubscribe: {type: String},
};
}
findPropertyName(attributeName) {
let resultName = attributeName;
const properties = this.constructor.properties;
for (const propertyName in properties) {
const attribute = properties[propertyName].attribute;
if (attribute === attributeName) {
resultName = propertyName;
break;
}
}
return resultName;
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case 'subscribe':
if (this.subscribe && this.subscribe.length > 0) {
if (this.connected) {
const attrs = this.subscribe.split(',');
attrs.forEach((element) => this.unSubscribeProviderFor(element));
} else {
this.deferUnSubscribe = this.subscribe.length > 0;
this.unsubscribe = this.subscribe;
}
}
if (newValue !== null) {
this.subscribe = newValue;
if (this.connected) {
const attrs = newValue.split(',');
attrs.forEach((element) => this.subscribeProviderFor(element));
} else {
this.deferSubscribe = newValue && newValue.length > 0;
}
}
break;
default:
break;
}
}
/**
* Send a set-property event to the provider components
*
* @param name
* @param value
* @returns {boolean}
*/
sendSetPropertyEvent(name, value) {
const event = new CustomEvent('set-property', {
bubbles: true,
composed: true,
detail: {name: name, value: value},
});
return this.dispatchEvent(event);
}
setPropertiesToChildNodes(local, value) {
let children = this.children;
Array.from(children).forEach((child) => child.setAttribute(local, value));
}
}