Skip to content

Instantly share code, notes, and snippets.

@enjikaka
Created October 8, 2019 13:28
Show Gist options
  • Save enjikaka/c04d77a4df4ff31980898e18aab65c03 to your computer and use it in GitHub Desktop.
Save enjikaka/c04d77a4df4ff31980898e18aab65c03 to your computer and use it in GitHub Desktop.
web.js from declinded PR
/**
* Converts a snake-case string to camelCase
*
* @param {string} str kebab-cased string
* @returns {string} camelCased string
*/
export function kebabToCamelCase(str) {
return str.replace(/(-)([a-z])/g, g => g[1].toUpperCase());
}
/**
* Converts a camelCase string to kebab-case
*
* @param {string} str camelCased string
* @returns {string} kebab-cased string
*/
export function camelToKebabCase(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* Takes attributes from element and creates an object
* with the keys camelCased.
*
* @param {NamedNodeMap} attributes Element.attributes
* @returns {object} Object with camelCased keys
*/
export function attributesToObject(attributes) {
return attributes
? Array.from(attributes).reduce(
(cur, { localName, value }) => ({
...cur,
[kebabToCamelCase(localName)]: value,
}),
{},
)
: {};
}
/**
* Takes a class for an extended Component/HTMLElement
* and registes it basedof the ClassName as class-name.
*
* @param {Function} classDef Class of a custom element to register
* @returns {string} Name of custom element.
*/
export function registerComponent(classDef) {
const kebabName = camelToKebabCase(classDef.prototype.constructor.name);
customElements.define(kebabName, classDef);
/*
By returning the kebab-name a custom element can easily used in React by
using the default export of a web component which specs the default exports like so:
export default registerComponent(renderFunction)
Usage in react like:
import CoolWebComp from 'cool-web-comp.js';
return (<CoolWebComp />); // <- JSX
*/
return kebabName;
}
/**
* Abstraction for React-ish methods.
*
* Can be extended later with props hadling when needed, already available in google-cast-receiver repo.
*/
export class Component extends HTMLElement {
connectedCallback() {
this.sDOM = this.attachShadow({ mode: 'closed' });
if (this.render) {
this.sDOM.innerHTML = this.render();
// Double rAF is a slightly working hack for executing stuff after browser has rendered.
requestAnimationFrame(() =>
requestAnimationFrame(() => {
if (this.componentDidMount) {
this.componentDidMount();
}
}),
);
}
}
}
/**
* Takes a function which will be used for rendering, similar to function components in React.
* The name of the function is used to make a custom element and the methods result is injected
* to the ShadowDOM.
*
* @param {Function} renderFunction Function that return innerHTML for ShadowDOM.
* @returns {string} Name of custom element.
*/
export function registerFunctionComponent(renderFunction) {
const kebabName = camelToKebabCase(renderFunction.prototype.constructor.name);
customElements.define(
kebabName,
class extends Component {
render() {
return renderFunction({
props: attributesToObject(this.attributes),
});
}
},
);
/*
By returning the kebab-name a custom element can easily used in React by
using the default export of a web component which specs the default exports like so:
export default registerFunctionComponent(renderFunction)
Usage in react like:
import CoolWebComp from 'cool-web-comp.js';
return (<CoolWebComp />); // <- JSX
*/
return kebabName;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment