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

https://github.com/qrailibs/bubblejs

✨ Frontend framework that embeds everywhere & fully type-safe.
https://github.com/qrailibs/bubblejs

framework mvc mvvm typescript webcomponent

Last synced: 9 months ago
JSON representation

✨ Frontend framework that embeds everywhere & fully type-safe.

Awesome Lists containing this project

README

          

# BubbleJS
Frontend framework based on WebComponents with full support of TypeScript.

# Features
- [Components](#components)
- [`Refs`](#refs)
- [`WatchRefs`](#watchrefs)
- [`MapRefs`](#maprefs)
- [`CachedRefs`](#cachedrefs)
- [`Props`](#props)
- [`Elements`](#elements)
- [`Events`](#events)
- [`Slots`](#slots)
- [`Binding`](#binding)
- [Conditional elements (`--if`, `--else`)](#conditional-elements)
- [Conditional attributes (`attr:if`)](#conditional-attributes)
- [Loops](#loops)
- [Routing & Views](#routing)
- [Directives](#directives)
- [Stores](#stores)
- [Caching](#caching)

## Components
Components are classes that represents WebComponents. To define your component use a `Component` decorator with tag name of component as argument:

```ts
import { ComponentBase, Component, Ref, html } from 'bubblejs-core';

@Component('my-component')
class MyComponent extends ComponentBase {
@Ref()
public count: number = 0;

template() {
return html`

Clicked ${this.count} times

`;
}

onClick() {
this.count++;
}

// Lifecycle hooks
onBeforeMount() {
console.log('Before mounted');
}
onMount() {
console.log('Mounted');
}
onAdopt() {
console.log('Adopted');
}
onDestroy() {
console.log('Destroyed');
}

styles() {
return /*css*/`
button {
padding: 12px 20px;

background: #4938ff;
color: white;

border: none;
border-radius: 8px;
outline: none;

cursor: pointer;

font-size: 16px;

transition: 0.3s ease-in-out;
}
button:hover {
background: #6456fb;
}
`;
}
}
```
```html

```

When you define a component class, you should define a `template()` method, that returns HTML template that will be rendered.
The `styles()` method is optional and used to define CSS of your component.

## Refs
Refs are simply reactive data variables, that can be used inside component. To define a ref use a `Ref` decorator:

```ts
import { ComponentBase, Component, Ref, html } from 'bubblejs-core';

@Component('my-component')
class MyComponent extends ComponentBase {
@Ref()
public count: number = 0;

template() {
return html`

Count is ${ this.count }

`;
}

onMount() {
setInterval(() => {
this.count++
}, 100);
}
}
```
```html

```

Refs can be accessed via `this` context inside `template`, lifecycle hooks, event listeners, methods.

For more complex scenarios `Ref` has callbacks that you can use:
- `init` (Called before mount, when value of ref should be initialized)
- `get` (Called when someone is trying to get ref value)
- `set` (Called when someone is trying to change ref value)
```ts
import { ComponentBase, Component, Ref, html } from 'bubblejs-core';

@Component('my-component')
class MyComponent extends ComponentBase {
@Ref({
init(key) => console.log('Initialized ref'),
get(key) => console.log('Trying to get ref value'),
set(key, newVal, oldVal) => console.log('Trying to change ref value'),
})
public count: number = 0;

template() {
return html`

Count is ${ this.count }

`;
}

onMount() {
setInterval(() => {
this.count++
}, 100);
}
}
```

## WatchRefs
...

## Props
Props allows to define acceptable properties of your component. To define a component prop use a `Prop` decorator:

```ts
import { ComponentBase, Component, Prop, html } from 'bubblejs-core';

@Component('my-component')
class MyComponent extends ComponentBase {
@Prop()
public text: string = '';

template() {
return html`
${ this.text }
`;
}
}
```
```html

```

**Notice**: you can pass arrays/objects/functions as prop value also, but only inside another component template.

Props can be accessed via `this` context inside `template`, lifecycle hooks, event listeners, methods.

## Elements
Elements are simply refs that stores html elements. You can use them to automatically get html element from your template and work with it.
```ts
import { ComponentBase, Component, Element, html } from 'bubblejs-core';

@Component('my-component')
class MyComponent extends ComponentBase {
@Element('.some-input')
public inputEl?: HTMLInputElement;

template() {
return html`




`;
}

onMount() {
if(this.inputEl) {
this.inputEl.value = 'hello world!';
}
}
}
```
## Events
Events allows you to emit custom events up to parent component.
You just have to call `this.emit(eventName, ...args)` method to emit your event.
```ts
import { ComponentBase, Component, html } from 'bubblejs-core';

@Component('my-child')
class MyChild extends ComponentBase {
template() {
return html`

Button!

`;
}

onClick(e: Event) {
e.preventDefault();

// Emit custom event
this.emit('some-event');
}
}

@Component('my-parent')
class MyParent extends ComponentBase {
template() {
return html`
console.log('fired!')}">
`;
}
}
```

**Notice**: `emit` method is calls native `dispatchEvent`, that means you can also
use native `addEventListener` to catch your component's custom event.

## Slots
in progress.

## Binding
in progress.

## Conditional elements
For conditional rendering framework is provides special reserved directives `--if` and `--else`. You should pass boolean value, if value will be `true`, the element will be rendered, otherwise it will render the `--else` element.
```ts
import { ComponentBase, Component, Ref, html } from './lib/index';

@Component('my-component')
class MyComponent extends ComponentBase {
@Ref()
public doRender: boolean = false;

template() {
return html`


Hello world!



Bye world!


`;
}
}
```
After render:
```html

Bye world!

```

## Conditional attributes
As you apply conditions to render or not elements, you can apply conditions to set attribute or not. Framework provides special syntax for conditional attributes: `attrName:if="${true | false}"`. If value will be true, the element will have `attrName=""` attribute, otherwise attribute will be not added.

```ts
import { ComponentBase, Component, Ref, html } from './lib/index';

@Component('my-component')
class MyComponent extends ComponentBase {
@Ref()
public isRequired: boolean = false;

@Ref()
public isDisabled: boolean = true;

template() {
return html`

`;
}
}
```
After render:
```html

```

## Loops
Loops can be helpful when you need to render a list of something. To create a loop you can use a native array `.map` method.
```ts

import { ComponentBase, Component, Ref, html } from 'bubblejs-core';

@Component('some-list')
class SomeList extends ComponentBase {
@Ref()
public items: string[] = [
'hello',
'world',
'guys'
];

template() {
return html`


${this.items.map((item) => html`

${item}



`)}

`;
}
}
```

**Notice**: you can use alternative ways to do a loops, but in any way the returned value should be of type `HtmlTemplate[]`.

## Routing
Framework also provides a built-in lightweight routing system.
Firstly you have to use `bubble-router` component which is renders
current view inside:
```html

```

After that you can define views in your code using `View` decorator:
```ts
import { ComponentBase, View, Ref, html } from 'bubblejs-core';

@View('/')
class IndexView extends ComponentBase {
@Ref()
public subject = 'world';

template() {
return html`

Hello ${subject}!


`;
}
}
```
As you see, views are basically components but defined in different way.

## Directives
-

## Stores
-

## Caching
-