Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

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.

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' }),
],
});
```