Skip to content
Snippets Groups Projects
Commit 39c1d1e5 authored by Steinwender, Tamara's avatar Steinwender, Tamara
Browse files

DBP Adapter for third party webcomponents. Not finished yet

parent 5fa241c4
No related branches found
No related tags found
No related merge requests found
...@@ -40,7 +40,7 @@ function getBuildInfo() { ...@@ -40,7 +40,7 @@ function getBuildInfo() {
export default (async () => { export default (async () => {
return { return {
input: (build != 'test') ? ['src/dbp-provider.js', 'src/dbp-provider-demo.js'] : glob.sync('test/**/*.js'), input: (build != 'test') ? ['src/dbp-provider.js', 'src/dbp-adapter.js','src/dbp-provider-demo.js'] : glob.sync('test/**/*.js'),
output: { output: {
dir: 'dist', dir: 'dist',
entryFileNames: '[name].js', entryFileNames: '[name].js',
......
export class Adapter 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;
this.callbackStore = [];
// Previously we used direct properties like this["lang"] (instead of this.propertyStore["lang"]) for storing the
// properties, but the "lang" property seems to be updated before the event from the MutationObserver, so we
// cannot observe a value change directly (as workaround we use another property (e.g. "langValue") instead of "lang")
this.propertyStore = {};
// We need to store our own "last values" because we cannot be sure what the MutationObserver detects
this.lastProperties = {};
console.log('Adapter constructor()');
}
getProperty(name) {
return this.propertyStore[name];
}
getPropertyByAttributeName(name) {
return this[this.findPropertyName(name)];
}
setProperty(name, value) {
if (typeof value === 'object' && value !== null) {
// console.log("value is object", value);
this.setPropertyByAttributeName(name, value);
} else {
this.attributeChangedCallback(name, this.getPropertyByAttributeName(name), value);
}
this.lastProperties[name] = value;
this.propertyStore[name] = value;
}
hasPropertyChanged(name, value) {
return this.lastProperties[name] !== value;
}
hasProperty(name) {
// return this.hasAttribute("name")
return Object.hasOwnProperty.call(this.propertyStore, name);
}
connectedCallback() {
if (this.deferUnSubscribe) {
const attrs = this.unsubscribe.split(',');
attrs.forEach(element => this.unSubscribeProviderFor(element));
this.deferSubscribe = false;
this.unsubscribe = '';
}
if (this.deferSubscribe) {
const attrs = this.subscribe.split(',');
attrs.forEach(element => this.subscribeProviderFor(element));
this.deferSubscribe = false;
}
this.connected = true;
const that = this;
// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: false, subtree: false };
// Callback function to execute when mutations are observed
const callback = function(mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for(const mutation of mutationsList) {
if (mutation.type === 'attributes') {
const name = mutation.attributeName;
const value = that.getAttribute(name);
if (that.hasPropertyChanged(name, value)) {
console.log('AdapterProvider (' + that.tagName + ') observed attribute "' + name + '" changed');
that.setProperty(name, value);
that.callbackStore.forEach(item => {
if (item.name === name) {
item.callback(value);
}
});
}
}
}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(this, config);
// 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.setProperty(attrs[i].name, attrs[i].value);
console.log('AdapterProvider (' + that.tagName + ') found attribute "' + attrs[i].name + '" = "' + attrs[i].value + '"');
}
}
}
disconnectedCallback() {
const attrs = this.subscribe.split(',');
attrs.forEach(element => this.unSubscribeProviderFor(element));
super.disconnectedCallback();
}
subscribeProviderFor(element) {
console.log('AdapterProvider(' + this.tagName + ') subscribeProviderFor( ' + element + ' )');
const pair = element.trim().split(':');
const local = pair[0];
const global = pair[1] || local;
const that = this;
const event = new CustomEvent('subscribe',
{
bubbles: true,
composed: true,
detail: {
name: global,
callback: (value) => {
console.log('AdapterProvider(' + that.tagName + ') sub/Callback ' + global + ' -> ' + local + ' = ' + value);
that.setPropertiesToChildNodes(local, value);
// If value is an object set it directly as property
if (typeof value === 'object' && value !== null) {
// console.log("value is object", value);
that.setPropertyByAttributeName(local, value);
} else {
// console.log("local, that.getPropertyByAttributeName(local), value", local, that.getPropertyByAttributeName(local), value);
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) {
console.log('AdapterProvider(' + this.tagName + ') unSubscribeProviderFor( ' + element + ' )');
const pair = element.trim().split(':');
const global = pair[1] || pair[0];
const event = new CustomEvent('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;
// console.log("properties", properties);
for (const propertyName in properties) {
// console.log("findPropertyName", `${propertyName}: ${properties[propertyName]}`);
const attribute = properties[propertyName].attribute;
if (attribute === attributeName) {
resultName = propertyName;
break;
}
}
return resultName;
}
attributeChangedCallback(name, oldValue, newValue) {
switch(name) {
case 'subscribe':
console.log('AdapterProvider() attributeChangesCallback( ' + name + ', ' + oldValue + ', ' + newValue + ')');
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;
//super.attributeChangedCallback(name, oldValue, newValue);
}
// console.log("this.lang", this.tagName, name, this.lang);
// console.log("this.entryPointUrl", this.tagName, name, this.entryPointUrl);
// console.trace();
}
/**
* 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));
}
}
import * as commonUtils from '@dbp-toolkit/common/utils';
import {Adapter} from './adapter.js';
commonUtils.defineCustomElement('dbp-provider-adapter', Adapter);
...@@ -5,6 +5,7 @@ import {AuthKeycloak, LoginButton} from '@dbp-toolkit/auth'; ...@@ -5,6 +5,7 @@ import {AuthKeycloak, LoginButton} from '@dbp-toolkit/auth';
import * as commonUtils from '@dbp-toolkit/common/utils'; import * as commonUtils from '@dbp-toolkit/common/utils';
import * as commonStyles from '@dbp-toolkit/common/styles'; import * as commonStyles from '@dbp-toolkit/common/styles';
import {Provider} from '@dbp-toolkit/provider'; import {Provider} from '@dbp-toolkit/provider';
import {Adapter} from '@dbp-toolkit/provider';
import {LanguageSelect} from '@dbp-toolkit/language-select'; import {LanguageSelect} from '@dbp-toolkit/language-select';
import DBPLitElement from "@dbp-toolkit/common/dbp-lit-element"; import DBPLitElement from "@dbp-toolkit/common/dbp-lit-element";
...@@ -22,6 +23,7 @@ class ProviderDemo extends ScopedElementsMixin(DBPLitElement) { ...@@ -22,6 +23,7 @@ class ProviderDemo extends ScopedElementsMixin(DBPLitElement) {
'dbp-login-button': LoginButton, 'dbp-login-button': LoginButton,
'dbp-language-select': LanguageSelect, 'dbp-language-select': LanguageSelect,
'dbp-provider': Provider, 'dbp-provider': Provider,
'dbp-provider-adapter': Adapter,
'dbp-consumer': DemoConsumer, 'dbp-consumer': DemoConsumer,
}; };
} }
...@@ -110,6 +112,23 @@ class ProviderDemo extends ScopedElementsMixin(DBPLitElement) { ...@@ -110,6 +112,23 @@ class ProviderDemo extends ScopedElementsMixin(DBPLitElement) {
</div> </div>
</dbp-provider> </dbp-provider>
</dbp-provider> </dbp-provider>
<h2> DBP Provider </h2>
<p> The dbp-provider is for third party webcomponents, which we want to configure with a provider.</p>
<pre>&lt;dbp-provider id="demoadapter" dbp-style-red="color:red;" dbp-style-green="color:green;" >&lt;/dbp-provider&gt;</pre>
<dbp-provider id="demoadapter"
dbp-style-red="color:red;" dbp-style-green="color:green;">
<pre>&lt;dbp-provider-adapter id="a1" subscribe="style:dbp-style-red" >&lt;/dbp-provider-adapter&gt;</pre>
<dbp-provider-adapter id="a1" subscribe="style:dbp-style-red">
<p> I'm a normal p tag without attributes and without style. </p>
<p> I'm a normal p tag without attributes and without style. </p>
<p> I'm a normal p tag without attributes and without style. </p>
</dbp-provider-adapter>
<pre>&lt;dbp-provider-adapter id="a2" subscribe="style:dbp-style-green" >&lt;/dbp-provider-adapter&gt;</pre>
<dbp-provider-adapter id="a2" subscribe="style:dbp-style-green">
<p style="background-color:green;"> I'm a normal p tag without attributes and without style. <span style="color:blue;"> I'm blue dabedidabedei...</span> </p>
</dbp-provider-adapter>
</dbp-provider>
</section> </section>
`; `;
} }
......
import {Provider} from './provider.js'; import {Provider} from './provider.js';
import {Adapter} from './adapter.js';
export {Provider}; export {Provider};
\ No newline at end of file export {Adapter};
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment