This project was born based on the need to create a Deisgn System to be used within Take, the components are written in Web Components using the StencilJS library created by the Ionic team.
This project was created using stencil-app-starter.
Stencil is a compiler for building fast web apps using Web Components.
Stencil combines the best concepts of the most popular frontend frameworks into a compile-time rather than run-time tool. Stencil takes TypeScript, JSX, a tiny virtual DOM layer, efficient one-way data binding, an asynchronous rendering pipeline (similar to React Fiber), and lazy-loading out of the box, and generates 100% standards-based Web Components that run in any browser supporting the Custom Elements v1 spec.
Stencil components are just Web Components, so they work in any major framework or with no framework at all.
Blip-DS is available as an npm package.
npm i blip-ds
There is not much secret, if you need something more detailed I recommend reading the documentação do StencilJS.
File: main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
// Note: loader import location set using "esmLoaderPath" within the output target confg
import { applyPolyfills, defineCustomElements } from 'blip-ds/loader';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
applyPolyfills().then(() => {
defineCustomElements(window)
})
File: app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule { }
Examples
File: app.component.html
<div>
<div class="container-count">
<bds-typo variant="fs-16">{{ count }}</bds-typo>
<bds-button (click)="addCount()">+</bds-button>
<bds-button (click)="subCount()">-</bds-button>
<bds-input value={{count}} (bdsChange)="onChange($event)"></bds-input>
</div>
</div>
File: app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
count = 0;
addCount() {
this.count++;
}
subCount() {
this.count--;
}
onChange(event) {
this.count = event.target.value;
}
}
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { applyPolyfills, defineCustomElements } from 'blip-ds/loader';
ReactDOM.render(<App />, document.getElementById('root'));
applyPolyfills().then(() => {
defineCustomElements(window);
});
Examples
React has a big problem working with web components, as properties and events are not handled correctly. The solution to these two problems is to create a web component wrapper with a React component, and thus using the Ref of the custom element. As in the example below:
const InputExample = ({ onInput, onChange, value }) => {
const elementRf = useRef(null);
useEffect(() => {
elementRf.current.value = value;
elementRf.current.addEventListener('bdsChange', (event) => {
onChange(event);
});
elementRf.current.addEventListener('bdsInput', (event) => {
console.log('bdsInput', { event })
});
}, [value, onChange, onInput]);
return <bds-input ref={elementRf} value={value}></bds-input>
}
Here we create a functional component named InputExample to be the wrapper for the Custom Element bds-input.
function CountBlipDS() {
const [count, changeCount] = useState(0);
return (
<div>
<p> {count} </p>
<InputExample value={count} onChange={(event) => { changeCount(event.target.value) }}></InputExample>
</div>
);
}
import Vue from 'vue'
import { applyPolyfills, defineCustomElements } from 'blip-ds/loader';
import App from './App.vue'
Vue.config.productionTip = false
Vue.config.ignoredElements = [/bds-\w*/];
applyPolyfills().then(() => {
defineCustomElements(window);
});
new Vue({
render: h => h(App),
}).$mount('#app')
The components should then be available in any of the Vue components
Examples
<template>
<div id="app">
{{ count }}
<bds-button v-on:click="count++">+</bds-button>
<bds-button v-on:click="count--">-</bds-button>
<bds-input v-bind:value="count" v-on:bdsChange="change"></bds-input>
</div>
</template>
<script>
export default {
name: "app",
components: {},
data: function() {
return {
count: 0
};
},
methods: {
change(event) {
this.count = event.target.value;
}
}
};
</script>
<style>
#app {
display: flex;
justify-content: space-around;
align-items: center;
}
</style>
}
To start building a new web component:
Run:
npm install
npm start
Run with Storybook
npm story
To build the component for production, run:
npm run build
To run the unit tests for the components, run:
npm test
Create component:
npm run generate
blip-ds
is under the ISC license.