Ecosyste.ms: Awesome

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

Awesome Lists | Featured Topics | Projects

https://github.com/nathanjhood/ts-web-components

How to OOP in the browser; with types.
https://github.com/nathanjhood/ts-web-components

no-config nodejs tailwindcss typescript web-component web-component-starter web-components

Last synced: 28 days ago
JSON representation

How to OOP in the browser; with types.

Awesome Lists containing this project

README

        

# Typescript Web Components

Typed OOP in the browser!

```html

(...)



"Your App Goes Here!"



```

- No config
- No framework
- No abstractions

Just raw [Web API](https://developer.mozilla.org/en-US/docs/Web/API).

Powered by Typescript, TailwindCSS, ESBuild, and fast-refreshing development server!

## How to...

Step by step!

- [`start`](#start)
- [`class AppComponent {}`](#class-appcomponent-)
- [`AppComponent.constructor()`](#appcomponentconstructor)
- [`AppComponent extends HTMLElement`](#appcomponent-extends-htmlelement)
- [`HTMLElement.super()`](#htmlelementsuper)
- [`this`](#this)
- [`AppComponent.innerHtml`](#appcomponentinnerhtml)
- [`CustomElementRegistry`](#customelementregistry)
- [`'app-component': AppComponent`](#app-component-appcomponent)
- [`AppComponent.render()`](#appcomponentrender)
- [`AppComponent.render(innerHTML)`](#appcomponentrenderinnerhtml)
- [`AppComponent.setup()`](#appcomponentsetup)
- [`createElement('app-component')`](#createelementapp-component)
- [`App()`](#app)
- [`render(App)`](#renderapp)
- [``](#app-component)
- [Tips](#tips)
- [Further Reading](#further-reading)

### `start`

```sh
$ git clone [email protected]:nathanjhood/ts-web-components.git
```

```sh
$ cd ts-web-components
```

```sh
$ npm install
```

```sh
# For Windows...
$env:NODE_ENV="development"

# For Linux/Mac...
export NODE_ENV="development"
```

```sh
$ npm run start
```
```sh
# ...

Rebuilding...

Done in 1623ms.

Server running at http://127.0.0.1:3000/
To exit: Ctrl + c

```

[Open in your browser](http://localhost:3000) and edit `src/App.ts` - the page will automatically refresh itself after every save.

---

### `class AppComponent {}`

```ts
// src/App.ts

class AppComponent {}
```

---

### `AppComponent.constructor()`

```ts
class AppComponent {
constructor() {/** setup goes here... */}
}
```

---

### `AppComponent extends HTMLElement`

```ts
// "I am a HTMLElement"

class AppComponent extends HTMLElement {
constructor() {}
}

// "...plus more ;) "
```

---

### `HTMLElement.super()`

```ts
class AppComponent extends HTMLElement {
constructor() {
super(); // MUST do this first...
}
}
```

---

### `this`

```ts
class AppComponent extends HTMLElement {
constructor() {
// inside here, "this" means "this 'AppComponent'"...

super();
this. // <-- '.' should produce a long list of props and methods...
}
}
```

---

### `AppComponent.innerHtml`

```ts
// 'innerHTML' === innerHTML

class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = `Your app goes here`;
}
}
```

### `CustomElementRegistry`

```ts
// IMPORTANT
window.customElements.define('app-component', AppComponent);
```

---

### `'app-component': AppComponent`

```ts
window.customElements.define('app-component', // <-- wrap the class!
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = `Your app goes here`;
}
}
); // <-- '.define()' ends here!
```

---

---

### `AppComponent.render()`

```ts
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = this.render();
}
render() {
return `Your app goes here`;
}
}
);
```

---

### `AppComponent.render(innerHTML)`

```ts
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML: string): string {
return `${innerHTML}`;
}
}
);
```

---

### `AppComponent.setup()`

```ts
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.setup();
}
setup(): void {
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML: string): string {
return `${innerHTML}`;
}
}
);
```

---

### `createElement('app-component')`

```ts
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.setup();
}
setup(): void {
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML: string): string {
return `${innerHTML}`;
}
}
);

const app = document.createElement('app-component');
```

---

### `App()`

```ts
const App = () => {
// define the component
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.setup();
}
setup(): void {
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML: string): string {
return `${innerHTML}`;
}
}
);
// then return it
return document.createElement('app-component');
}

// Now we can assign it :)
const app = App();
```

---

### `render(App)`

```ts
// src/index.ts

import App = require('./App');

const render = (element: () => HTMLElement) = {
// ...attaches passed-in element to document
}

// so, pass it our App :)
render(App)

```

---

### ``

```html





```

```html





#shadowRoot (open)
"Your App Goes Here!"



```

## Tips

---

### functional approach

```ts
// example:

const Button = (): HTMLButtonElement => {
return document.createElement('button')
}

// HTMLButtonElement
const button = Button();

```

---

### factory method

```ts
// example

const CustomButton = () => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
}
}
customElements.define('custom-button', CustomButtonElement)
return document.createElement('custom-button') as CustomButtonElement;
};

// CustomButtom
const customButton = CustomButton();
```

---

### passing props

```ts
type CustomButtonProps = {
type: 'submit' | 'reset' | 'button';
};

const CustomButton = (props: CustomButtonProps) => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
this.type = props.type;
}
}
customElements.define('custom-button', CustomButtonElement);
return document.createElement('custom-button') as CustomButtonElement;
};

const customButton = CustomButton({ type: 'submit' });

```

---

### adding children

```ts
type CustomButtonProps = {
type: 'submit' | 'reset' | 'button';
children?: Node;
};

const CustomButton = (props: CustomButtonProps) => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
this.type = props.type;
if (props.children) this.appendChild(props.children);
}
}
customElements.define('custom-button', CustomButtonElement);
return document.createElement('custom-button') as CustomButtonElement;
};

const customButtonA = CustomButton({ type: 'submit' });
const customButtonB = CustomButton({ type: 'submit', children: customButtonA });

```

---

### adding styles

```ts
type CustomButtonProps = {
type: 'submit' | 'reset' | 'button';
children?: Node;
className?: string;
};

const CustomButton = (props: CustomButtonProps) => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
this.type = props.type;
if (props.children) this.appendChild(props.children);
if (props.className) this.className = props.className;
}
}
customElements.define('custom-button', CustomButtonElement);
return document.createElement('custom-button') as CustomButtonElement;
};

const tailwindButton = CustomButton({
type: 'submit',
className: 'flex align-left text-white bg-red-500',
});

```

---

## Further Reading

- [webcomponents.org](https://www.webcomponents.org/)
- [eisenebergeffect @ Medium: Hello Web Components](https://eisenbergeffect.medium.com/hello-web-components-795ed1bd108e)
- [MDN's Web Component examples](https://github.com/mdn/web-components-examples)
- [MDN's Web API glossary](https://developer.mozilla.org/en-US/docs/Web/API)
- [MDN's Web API - HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)
- [Building Interoperable Web Components (including React)](https://css-tricks.com/building-interoperable-web-components-react/)

[Read me on github.com](https://github.com/nathanjhood/ts-web-components)

[Read me on nathanjhood.github.io](https://nathanjhood.github.io/ts-web-components)