Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rosenkolev/web-components-vite-app
An web application using only native browser technology and vite build tool. HTML-first,template-agnostic,web 2.0.
https://github.com/rosenkolev/web-components-vite-app
custom-elements vite web-components web2
Last synced: 3 months ago
JSON representation
An web application using only native browser technology and vite build tool. HTML-first,template-agnostic,web 2.0.
- Host: GitHub
- URL: https://github.com/rosenkolev/web-components-vite-app
- Owner: rosenkolev
- License: mit
- Created: 2024-06-21T12:19:01.000Z (7 months ago)
- Default Branch: main
- Last Pushed: 2024-06-25T15:14:37.000Z (7 months ago)
- Last Synced: 2024-10-02T06:42:30.154Z (4 months ago)
- Topics: custom-elements, vite, web-components, web2
- Language: JavaScript
- Homepage: https://rosenkolev.github.io/web-components-vite-app/
- Size: 84 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Web Components Application
An web application using only browser native technologies and no frameworks.
`fast` `web components` `browser native` `HTML template based` `vite`
This application demonstrate that the bowser's have evolved to support complex application without external dependecies.
# Component
The entire source code a component is in a single file (e.g. `home-page.component.html`).
```html
Home Page
class HomePage extends CustomElement {
static component = Object.freeze({
selector: 'home-page'
});
};
HomePage.componentInit();```
Use data attributes to bind and attach events.
__Example:__
- onclick event calls method onClick: `RED`
- bind color property to InnerText: ``
- bind color property to css color: ``
- bind color property to attrbiute, with default value BLACK: `````html
RED
BLUE
class TestPage extends CustomElement {
static component = Object.freeze({
selector: 'test-page'
});
onClick(color) {
this.state.setState({ color });
}onReset(color) {
this.state.setState({ color });
}
};
TestPage.componentInit();```
# index.html
All html files will be bundled into the `index.html` file.
```html
SPA WC Vite
window.DEBUG_CUSTOM_ELEMENTS=true
```
# core.js
The `core.js` creates the `CustomElement` class that provide abstraction for easier working with `Web Components`.
```javascript
window.core = {
isObject: function (obj) { /** check is object. */ },
toArray: function (obj) { /** covert list to array */ },
registerComponent: function (selector, element, dependencies, extend) {
/** abstraction that first register dependencies, than register the component */
window.customElements.define(selector, element, { extends: extend });
},
/** other methods */
};class StateManager {
constructor(onStateChanged) {
}setState(newState) {
}set(key, value) {
}
}class CustomElement extends HTMLElement {
static componentInit() {
this.prototype.template = document.getElementById(this.component.templateId || this.component.selector);
core.registerComponent(/** */);
}state = new StateManager(() => {
/* update all [data-bind], [data-bind-attributeName], [data-css], etc. */
});constructor() {
this.attachShadow({ mode: "open" });
this.shadowRoot.appendChild(this.template.content.cloneNode(true));// attach to all event listeners specified by [data-on]
this.shadowRoot.querySelectorAll("[data-on]").forEach((node) => {
node.addEventListener(/** */);
});
}attributeChangedCallback(name, oldValue, newValue) {
if (oldValue !== newValue) {
this.state.set(name, newValue);
}
}
}
```# Vite
Vite is used only for bundeling files. In reality `vite` is not needed and can be done with a simple NodeJs script.
`package.json`
```json
{
"scripts": {
"start": "vite",
"build": "vite build"
},
"devDependencies": {
"vite": "5.1.7"
}
}
``````javascript
// vite.config.js
/** @param options {{ path: string, at: 'head' | 'body' | 'body-pre' }} */
function injectFilesInIndexHtml(options) {
return {
name: 'inject-files-in-index-html',
transformIndexHtml: {
transform(html) {
/** ... */
const isDirectory = options.path...;
const files = isDirectory ? fs.readdirSync(basePath) : [options.path];
const filesContent = files.map((file) => {
const pathToFile = path.resolve(basePath, file);
const txt = fs.readFileSync(pathToFile);
switch (file.split('.').pop()) {
/** ... */
case 'html':
return txt.toString();
}
});
const data = filesContent.join('');
switch (options.at) {
case 'head':
return html.replace('', `${data}\n`);
case 'body':
return html.replace('', `\n${data}`);
}
},
},
};
}export default defineConfig({
plugins: [
injectFilesInIndexHtml({ path: 'core.js', at: 'head' }),
injectFilesInIndexHtml({ path: 'components/', at: 'body-pre' }),
],
});
```