Skip to content
This repository has been archived by the owner on May 19, 2023. It is now read-only.

Commit

Permalink
feat: watchers
Browse files Browse the repository at this point in the history
  • Loading branch information
aidenybai committed Mar 2, 2021
1 parent 4d7813e commit e7ad9af
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 5 deletions.
13 changes: 10 additions & 3 deletions src/component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Directives, State, ASTNode } from './models/structs';
import { Directives, Watchers, State, ASTNode } from './models/structs';

import { directives } from './core/directive';
import compile from './core/compile';
Expand All @@ -10,11 +10,13 @@ import { setElementCustomProp } from './core/utils/elementCustomProp';
export class Component {
public state: State;
public directives: Directives;
public watchers: Watchers;
public ast?: ASTNode[];

constructor(state: State = {}) {
this.state = state;
this.directives = {};
this.watchers = {};
}

public mount(el: HTMLElement | string, proxify: boolean = true): State {
Expand All @@ -26,7 +28,9 @@ export class Component {
this.ast = compile(rootEl as HTMLElement, this.state);
this.state = { ...this.state, $render };

this.state = proxify ? reactive(this.state, this.render.bind(this)).proxy : this.state;
this.state = proxify
? reactive(this.state, this.render.bind(this), this.watchers).proxy
: this.state;
this.directives = { ...this.directives, ...directives };

this.render();
Expand All @@ -36,11 +40,14 @@ export class Component {
return this.state;
}

// Custom directive registration
public directive(name: string, callback: Function) {
this.directives[name.toUpperCase()] = callback;
}

public watch(name: string, callback: Function) {
this.watchers[name] = callback;
}

public render(props: string[] = Object.keys(this.state)) {
render(this.ast!, directives, this.state, props);
}
Expand Down
9 changes: 8 additions & 1 deletion src/core/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export const arrayEquals = (firstArray: unknown[], secondArray: unknown[]) => {
);
};

export const reactive = (state: State, callback: Function): RevocableProxy => {
export const reactive = (
state: State,
callback: Function,
watchers: Record<string, Function> = {}
): RevocableProxy => {
const handler = {
get(target: UnknownKV, key: string): unknown {
if (typeof target[key] === 'object' && target[key] !== null) {
Expand Down Expand Up @@ -47,6 +51,9 @@ export const reactive = (state: State, callback: Function): RevocableProxy => {

target[key] = value;
callback(props);
for (const [prop, watcher] of Object.entries(watchers)) {
if (props.includes(prop)) watcher();
}

return true;
},
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,29 @@ export const init = (
): void => {
const stateDirective = `${DIRECTIVE_PREFIX}state`;
const initDirective = `${DIRECTIVE_PREFIX}init`;
const watchDirective = `${DIRECTIVE_PREFIX}watch`;

const componentElements = element.querySelectorAll(`[${stateDirective}]`);
// Filter out uninit scopes only
const uninitializedComponents = [...componentElements].filter(
(el) => getElementCustomProp(el as HTMLElement, '__l') === undefined
);

for (let el of uninitializedComponents) {
for (const el of uninitializedComponents) {
const stateExpression = el.getAttribute(stateDirective);
const initExpression = el.getAttribute(initDirective);
const watchExpression = el.getAttribute(watchDirective);

// Parse state from state expression
const state = computeExpression(`${stateExpression || '{}'}`, el as HTMLElement, true)({});
const watchers = computeExpression(`${watchExpression || '{}'}`, el as HTMLElement, true)({});
const currentComponent = component(state);
for (const [directiveName, directiveCallback] of Object.entries(directives)) {
currentComponent.directive(directiveName, directiveCallback);
}
for (const [property, watcher] of Object.entries(watchers)) {
currentComponent.watch(property, watcher as Function);
}
currentComponent.mount(el as HTMLElement);

const init = initExpression
Expand Down
1 change: 1 addition & 0 deletions src/models/structs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { UnknownKV } from './generics';

export type Directives = Record<string, Function>;
export type Watchers = Record<string, Function>;
export type Components = Record<string, Function>;
export type State = UnknownKV;

Expand Down

0 comments on commit e7ad9af

Please # to comment.