Skip to content

Commit

Permalink
[refactor] rewrite Function Observer adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
TechQuery committed Jan 4, 2024
1 parent 06cf537 commit 1c22770
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 47 deletions.
4 changes: 3 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
.eslintrc.json
jest.config.ts
test/
preview/
.parcel-cache/
Contributing.md
docs/
.husky/
.github/
.vscode/
.vscode/
.vercel/
13 changes: 12 additions & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Demo & **GitHub template**: https://web-cell.dev/scaffold/
```shell
npm init -y
npm install dom-renderer mobx web-cell
npm install parcel -D
npm install parcel @parcel/config-default "@parcel/transformer-typescript-tsc" -D
```

#### `package.json`
Expand Down Expand Up @@ -73,6 +73,17 @@ npm install parcel -D
}
```

#### `.parcelrc`

```json
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
```

#### `source/index.html`

```html
Expand Down
40 changes: 40 additions & 0 deletions preview/Clock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { observable } from 'mobx';
import { Second } from 'web-utility';
import { component, observer } from '../source';

class ClockModel {
@observable
accessor time = new Date();

constructor() {
setInterval(() => (this.time = new Date()), Second);
}
}

const clockStore = new ClockModel();

export const FunctionClock = observer(() => {
const { time } = clockStore;

return (
<time dateTime={time.toJSON()}>
Function Clock: {time.toLocaleString()}
</time>
);
});

@component({ tagName: 'class-clock' })
@observer
export class ClassClock extends HTMLElement {
state = new ClockModel();

render() {
const { time } = this.state;

return (
<time dateTime={time.toJSON()}>
Class Clock: {time.toLocaleString()}
</time>
);
}
}
63 changes: 26 additions & 37 deletions preview/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,27 @@
import { configure, observable } from 'mobx';
import { component, observer } from '../source';
import { ClassClock, FunctionClock } from './Clock';

configure({ enforceActions: 'never' });

@component({ tagName: 'home-page' })
@observer
export class HomePage extends HTMLElement {
@observable
accessor time = '';

connectedCallback() {
setInterval(() => (this.time = new Date().toLocaleString()), 1000);
}

render() {
const { time } = this;

return (
<>
<h1>Hello Vanilla!</h1>
<div>
We use the same configuration as Parcel to bundle this
sandbox, you can find more info about Parcel
<a
href="https://parceljs.org"
target="_blank"
rel="noopener noreferrer"
>
here
</a>
.
</div>
<time>{time}</time>
</>
);
}
}
export const HomePage = () => (
<>
<h1>Hello Vanilla!</h1>
<div>
We use the same configuration as Parcel to bundle this sandbox, you
can find more info about Parcel
<a
href="https://parceljs.org"
target="_blank"
rel="noopener noreferrer"
>
here
</a>
.
</div>
<ul>
<li>
<FunctionClock />
</li>
<li>
<ClassClock />
</li>
</ul>
</>
);
3 changes: 3 additions & 0 deletions preview/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { DOMRenderer } from 'dom-renderer';
import { configure } from 'mobx';

import { HomePage } from './Home';

configure({ enforceActions: 'never' });

new DOMRenderer().render(<HomePage />, document.querySelector('#app')!);
67 changes: 59 additions & 8 deletions source/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { DOMRenderer, VNode } from 'dom-renderer';
import { DOMRenderer, DataObject, VNode } from 'dom-renderer';
import { autorun } from 'mobx';
import { CustomElement, isHTMLElementClass } from 'web-utility';

interface ComponentMeta extends ElementDefinitionOptions {
export interface ComponentMeta extends ElementDefinitionOptions {
tagName: string;
}

/**
* Class decorator of Web components
*/
export function component({ tagName, ...meta }: ComponentMeta) {
return <T extends CustomElementConstructor>(
Class: T,
Expand Down Expand Up @@ -35,18 +39,65 @@ export function component({ tagName, ...meta }: ComponentMeta) {
};
}

export function observer<T extends CustomElementConstructor>(
Class: T,
_: ClassDecoratorContext
) {
class ObserverComponent extends (Class as CustomElementConstructor) {
export type FunctionComponent<P extends DataObject = {}> = (props: P) => VNode;
export type FC<P extends DataObject = {}> = FunctionComponent<P>;

function wrapFunction<P>(func: FC<P>) {
return (props: P) => {
var tree: VNode,
renderer = new DOMRenderer();

const disposer = autorun(() => {
const newTree = func(props);

tree = tree ? renderer.patch(tree, newTree) : newTree;
});
const { unRef } = tree;

tree.unRef = node => (disposer(), unRef?.(node));

return tree;
};
}

export type ClassComponent = CustomElementConstructor;

function wrapClass<T extends ClassComponent>(Class: T) {
class ObserverComponent
extends (Class as ClassComponent)
implements CustomElement
{
protected disposers = [];

constructor() {
super();

const { update } = Object.getPrototypeOf(this);
// @ts-ignore
this.update = () => autorun(() => update.call(this));
this.update = () =>
this.disposers.push(autorun(() => update.call(this)));
}

disconnectedCallback() {
for (const disposer of this.disposers) disposer();
}
}
return ObserverComponent as unknown as T;
}

export type WebCellComponent = FunctionComponent | ClassComponent;

/**
* Class decorator of Web components for MobX
*/
export function observer<T extends WebCellComponent>(
func: T,
_: ClassDecoratorContext
): T;
export function observer<T extends WebCellComponent>(func: T): T;
export function observer<T extends WebCellComponent>(
func: T,
_?: ClassDecoratorContext
) {
return isHTMLElementClass(func) ? wrapClass(func) : wrapFunction(func);
}

1 comment on commit 1c22770

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for web-cell ready!

✅ Preview
https://web-cell-1bi48ds0d-techquery.vercel.app

Built with commit 1c22770.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.