Project status
Use case
Encapsulate your components as web-components.
Content
Installation
You can install via package manager, simply download the compiled version as zip file here and inject or request via cdn in HTML:
npm install web-component-wrapper
import {func, object} from 'clientnode/property-types'
import {property} from 'web-component-wrapper/decorator'
import {Web} from 'web-component-wrapper/Web'
export class MyWebComponent<
TElement = HTMLElement,
ExternalProperties extends Mapping<unknown> = Mapping<unknown>,
InternalProperties extends Mapping<unknown> = Mapping<unknown>
> extends Web<TElement, ExternalProperties, InternalProperties> {
static content = `
<div class="wrapper" on-click="this.rootInstance.onClick(event)">
<slot>Please provide a template to transclude.</slot>
</div>
`
@property({type: object})
options = {} as Options
@property({type: func})
onClick: (event: MouseEvent) => Promise<void> = NOOP
/**
* Defines dynamic getter and setter interface and resolves a configuration
* object. Initializes the map implementation.
*/
constructor() {
super()
/*
Babel property declaration transformation overwrites defined
properties at the end of an implicit constructor. So we have to
redefine them as long as we want to declare expected component
interface properties to enable static type checks.
*/
this.defineGetterAndSetterInterface()
}
/**
* Triggered when ever a given attribute has changed and triggers to update
* configured dom content.
* @param name - Attribute name which was updates.
* @param newValue - New updated value.
* @returns Returns when attribute has been updated.
*/
async onUpdateAttribute(name: string, newValue: string): Promise<void> {
await super.onUpdateAttribute(name, newValue)
// ...
}
/**
* Updates controlled dom elements.
* @param reason - Why an update has been triggered.
* @param resolveRendering - Indicates whether rendering should be resolved
* finally. Should be set to "false" via super calls in inherited render
* methods which do further dom manipulations afterward and resolve the
* rendering process by their own.
* @returns A promise resolving when rendering has finished. A promise may
* be needed for classes inheriting from this class.
*/
async render(reason = 'unknown', resolveRendering = true): Promise<void> {
await super.render(reason, false)
await this.waitForNestedComponentRendering()
// ...
await this.resolveRenderingPromiseIfSet(reason, resolveRendering)
}
// ...
}
customElements.define('my-web-component', MyWebComponent)
Examples
Load via CDN
<script
src="https://unpkg.com/web-component-wrapper@latest/dist/bundle/index.js"
></script>
Simple Web-Component
class MyGreeting extends webComponentWrapper.Web {
static doRender = true
static evaluateSlots = true
static determineRootBinding = false
static content = '<div>Hello ${rootInstance.greetingName}</div>'
}
// Alternative to the decorator syntax:
webComponentWrapper.property()({self: MyGreeting}, 'greetingName')
customElements.define('my-greeting', MyGreeting)
<my-greeting greeting-name="World"></my-greeting>
Simple React-Web-Component
class MyReactGreeting extends webComponentWrapper.ReactWeb {
static doRender = true
static evaluateSlots = true
// Content has a react component to wrap.
static content = ({name}) => <div>Hello {name}</div>
}
// Alternative to the decorator syntax:
webComponentWrapper.property()({self: MyReactGreeting}, 'name')
customElements.define('my-react-greeting', MyReactGreeting)
Data-Flow
Data can flow into a component via
- External property set
instance.value = 'value' - Trigger Events
instance.triggerEvent('click')
Data can be communicated back via:
- Properties
log.info(instance.value) - Observable events
instance.addEventListener('click', (event) => console.log(event.detail.value))
Configuring Data-Flow
A Web-Component-Wrapper component forwards (transformed) given properties into
a wrapped React component via props and reads data via provided callbacks
as part of props or as part of reacts ref object.