rask-ui 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/component.d.ts +5 -8
- package/dist/component.d.ts.map +1 -1
- package/dist/component.js +53 -40
- package/dist/createState.js +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +1 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Solid offers a simpler mental model with fine-grained reactivity. Updates don't
|
|
|
46
46
|
|
|
47
47
|
- Requires understanding special compiler transformations
|
|
48
48
|
- Special components for expressing dynamic UIs (`<Show>`, `<For>`, etc.)
|
|
49
|
-
-
|
|
49
|
+
- Different signatures for accessing reactive values: `count()` VS `state.count`
|
|
50
50
|
|
|
51
51
|
### RASK: Best of Both Worlds
|
|
52
52
|
|
|
@@ -66,6 +66,8 @@ RASK gives you:
|
|
|
66
66
|
- **No compiler magic** - Plain JavaScript/TypeScript
|
|
67
67
|
- **Simple mental model** - State updates trigger only affected components
|
|
68
68
|
|
|
69
|
+
Built on [Inferno JS](https://github.com/infernojs/inferno).
|
|
70
|
+
|
|
69
71
|
## Getting Started
|
|
70
72
|
|
|
71
73
|
### Installation
|
|
@@ -233,8 +235,6 @@ Reactive objects are implemented using JavaScript Proxies. When you access a pro
|
|
|
233
235
|
- `createMutation()` - Never destructure mutation objects
|
|
234
236
|
- `createView()` - Never destructure view objects
|
|
235
237
|
|
|
236
|
-
**Learn more:** This is the same design decision as [Solid.js reactive primitives](https://www.solidjs.com/tutorial/introduction_signals), which also use this pattern for fine-grained reactivity.
|
|
237
|
-
|
|
238
238
|
## API Reference
|
|
239
239
|
|
|
240
240
|
### Core Functions
|
package/dist/component.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VNode, Component, Props
|
|
1
|
+
import { VNode, Component, Props } from "inferno";
|
|
2
2
|
export declare function getCurrentComponent(): RaskComponent<any>;
|
|
3
3
|
export declare function onMount(cb: () => void): void;
|
|
4
4
|
export declare function onCleanup(cb: () => void): void;
|
|
@@ -8,20 +8,17 @@ declare class RaskComponent<P extends Props<any>> extends Component<P & {
|
|
|
8
8
|
}> {
|
|
9
9
|
private renderFn?;
|
|
10
10
|
private reactiveProps?;
|
|
11
|
-
private prevChildren;
|
|
12
11
|
private observer;
|
|
12
|
+
private isRendering;
|
|
13
13
|
contexts: Map<any, any>;
|
|
14
14
|
getChildContext(): any;
|
|
15
15
|
onMounts: Array<() => void>;
|
|
16
16
|
onCleanups: Array<() => void>;
|
|
17
|
+
private createReactiveProps;
|
|
17
18
|
componentDidMount(): void;
|
|
18
|
-
componentWillReceiveProps(nextProps: Readonly<{
|
|
19
|
-
children?: InfernoNode;
|
|
20
|
-
} & P & {
|
|
21
|
-
__component: RaskFunctionComponent<P>;
|
|
22
|
-
}>): void;
|
|
23
19
|
componentWillUnmount(): void;
|
|
24
|
-
|
|
20
|
+
componentDidUpdate(): void;
|
|
21
|
+
shouldComponentUpdate(nextProps: Props<any>): boolean;
|
|
25
22
|
render(): any;
|
|
26
23
|
}
|
|
27
24
|
export declare function createComponent(props: Props<any>, key?: string): VNode;
|
package/dist/component.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,
|
|
1
|
+
{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,EACL,SAAS,EACT,KAAK,EAEN,MAAM,SAAS,CAAC;AAMjB,wBAAgB,mBAAmB,uBAMlC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAMrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAMvC;AAED,MAAM,MAAM,qBAAqB,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,IAClD,CAAC,MAAM,MAAM,KAAK,CAAC,GACnB,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;AAEhC,cAAM,aAAa,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,CAAE,SAAQ,SAAS,CACzD,CAAC,GAAG;IAAE,WAAW,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAA;CAAE,CAC9C;IACC,OAAO,CAAC,QAAQ,CAAC,CAAc;IAC/B,OAAO,CAAC,aAAa,CAAC,CAAa;IACnC,OAAO,CAAC,QAAQ,CAEb;IACH,OAAO,CAAC,WAAW,CAAS;IAC5B,QAAQ,gBAAa;IACrB,eAAe;IAUf,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACjC,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAM;IACnC,OAAO,CAAC,mBAAmB;IAgC3B,iBAAiB,IAAI,IAAI;IAGzB,oBAAoB,IAAI,IAAI;IAG5B,kBAAkB;IAUlB,qBAAqB,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,OAAO;IAerD,MAAM;CAyCP;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,SAO9D"}
|
package/dist/component.js
CHANGED
|
@@ -22,10 +22,10 @@ export function onCleanup(cb) {
|
|
|
22
22
|
class RaskComponent extends Component {
|
|
23
23
|
renderFn;
|
|
24
24
|
reactiveProps;
|
|
25
|
-
prevChildren;
|
|
26
25
|
observer = new Observer(() => {
|
|
27
26
|
this.forceUpdate();
|
|
28
27
|
});
|
|
28
|
+
isRendering = false;
|
|
29
29
|
contexts = new Map();
|
|
30
30
|
getChildContext() {
|
|
31
31
|
const parentGetContext = this.context.getContext;
|
|
@@ -38,32 +38,65 @@ class RaskComponent extends Component {
|
|
|
38
38
|
}
|
|
39
39
|
onMounts = [];
|
|
40
40
|
onCleanups = [];
|
|
41
|
+
createReactiveProps() {
|
|
42
|
+
const reactiveProps = {};
|
|
43
|
+
const self = this;
|
|
44
|
+
for (const prop in this.props) {
|
|
45
|
+
const signal = new Signal();
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
let reactiveValue = this.props[prop];
|
|
48
|
+
Object.defineProperty(reactiveProps, prop, {
|
|
49
|
+
get() {
|
|
50
|
+
if (!self.isRendering) {
|
|
51
|
+
const observer = getCurrentObserver();
|
|
52
|
+
if (observer) {
|
|
53
|
+
observer.subscribeSignal(signal);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
return self.props[prop];
|
|
58
|
+
},
|
|
59
|
+
set(value) {
|
|
60
|
+
if (reactiveValue !== value) {
|
|
61
|
+
reactiveValue = value;
|
|
62
|
+
signal.notify();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return reactiveProps;
|
|
68
|
+
}
|
|
41
69
|
componentDidMount() {
|
|
42
70
|
this.onMounts.forEach((cb) => cb());
|
|
43
71
|
}
|
|
44
|
-
componentWillReceiveProps(nextProps) {
|
|
45
|
-
if (this.props.children === nextProps.children) {
|
|
46
|
-
for (const prop in nextProps) {
|
|
47
|
-
if (prop === "children") {
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
// @ts-ignore
|
|
51
|
-
this.reactiveProps[prop] = nextProps[prop];
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
this.prevChildren = this.props.children;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
72
|
componentWillUnmount() {
|
|
59
73
|
this.onCleanups.forEach((cb) => cb());
|
|
60
74
|
}
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
componentDidUpdate() {
|
|
76
|
+
for (const prop in this.props) {
|
|
77
|
+
if (prop === "__component" || prop === "children") {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
// @ts-ignore
|
|
81
|
+
this.reactiveProps[prop] = this.props[prop];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
shouldComponentUpdate(nextProps) {
|
|
85
|
+
// Shallow comparison of props, excluding internal props
|
|
86
|
+
for (const prop in nextProps) {
|
|
87
|
+
if (prop === "__component") {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
// @ts-ignore
|
|
91
|
+
if (this.props[prop] !== nextProps[prop]) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
63
96
|
}
|
|
64
97
|
render() {
|
|
65
98
|
if (!this.renderFn) {
|
|
66
|
-
this.reactiveProps = createReactiveProps(
|
|
99
|
+
this.reactiveProps = this.createReactiveProps();
|
|
67
100
|
currentComponent = this;
|
|
68
101
|
try {
|
|
69
102
|
this.renderFn = this.props.__component(this.reactiveProps);
|
|
@@ -83,7 +116,9 @@ class RaskComponent extends Component {
|
|
|
83
116
|
const stopObserving = this.observer.observe();
|
|
84
117
|
let result = null;
|
|
85
118
|
try {
|
|
119
|
+
this.isRendering = true;
|
|
86
120
|
result = this.renderFn();
|
|
121
|
+
this.isRendering = false;
|
|
87
122
|
}
|
|
88
123
|
catch (error) {
|
|
89
124
|
if (typeof this.context.notifyError !== "function") {
|
|
@@ -100,25 +135,3 @@ class RaskComponent extends Component {
|
|
|
100
135
|
export function createComponent(props, key) {
|
|
101
136
|
return createComponentVNode(4 /* VNodeFlags.ComponentClass */, RaskComponent, props, key);
|
|
102
137
|
}
|
|
103
|
-
function createReactiveProps(props) {
|
|
104
|
-
const reactiveProps = {};
|
|
105
|
-
for (const prop in props) {
|
|
106
|
-
const signal = new Signal();
|
|
107
|
-
Object.defineProperty(reactiveProps, prop, {
|
|
108
|
-
get() {
|
|
109
|
-
const observer = getCurrentObserver();
|
|
110
|
-
if (observer) {
|
|
111
|
-
observer.subscribeSignal(signal);
|
|
112
|
-
}
|
|
113
|
-
return props[prop];
|
|
114
|
-
},
|
|
115
|
-
set(value) {
|
|
116
|
-
if (props[prop] !== value) {
|
|
117
|
-
props[prop] = value;
|
|
118
|
-
signal.notify();
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
return reactiveProps;
|
|
124
|
-
}
|
package/dist/createState.js
CHANGED
|
@@ -27,7 +27,7 @@ export function createState(state) {
|
|
|
27
27
|
return getProxy(state);
|
|
28
28
|
}
|
|
29
29
|
const proxyCache = new WeakMap();
|
|
30
|
-
const PROXY_MARKER = Symbol(
|
|
30
|
+
const PROXY_MARKER = Symbol("isProxy");
|
|
31
31
|
function getProxy(value) {
|
|
32
32
|
// Check if already a proxy to avoid double-wrapping
|
|
33
33
|
if (PROXY_MARKER in value) {
|
|
@@ -70,12 +70,13 @@ function getProxy(value) {
|
|
|
70
70
|
return Reflect.set(target, key, newValue);
|
|
71
71
|
}
|
|
72
72
|
const oldValue = Reflect.get(target, key);
|
|
73
|
+
const setResult = Reflect.set(target, key, newValue);
|
|
73
74
|
// We only notify if actual change, though array length actually updates under the hood
|
|
74
75
|
if (newValue !== oldValue || (Array.isArray(value) && key === "length")) {
|
|
75
76
|
const signal = signals[key];
|
|
76
77
|
signal?.notify();
|
|
77
78
|
}
|
|
78
|
-
return
|
|
79
|
+
return setResult;
|
|
79
80
|
},
|
|
80
81
|
deleteProperty(target, key) {
|
|
81
82
|
if (typeof key === "symbol") {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,aAEjC;AAKD,qBAAa,MAAM;IACjB,OAAO,CAAC,WAAW,CAAyB;IAC5C,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI;IAOxB,MAAM;
|
|
1
|
+
{"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../src/observation.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,aAEjC;AAKD,qBAAa,MAAM;IACjB,OAAO,CAAC,WAAW,CAAyB;IAC5C,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI;IAOxB,MAAM;CAIP;AAED,qBAAa,QAAQ;IACnB,UAAU,UAAS;IACnB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ,CAAa;gBACjB,QAAQ,EAAE,MAAM,IAAI;IAGhC,eAAe,CAAC,MAAM,EAAE,MAAM;IAG9B,OAAO;IAOP,OAAO;CAIR"}
|
package/dist/observation.js
CHANGED
|
@@ -14,18 +14,7 @@ export class Signal {
|
|
|
14
14
|
}
|
|
15
15
|
notify() {
|
|
16
16
|
const currentSubscribers = Array.from(this.subscribers);
|
|
17
|
-
|
|
18
|
-
currentSubscribers.forEach((cb) => cb());
|
|
19
|
-
});
|
|
20
|
-
if (!isQueuingNotify) {
|
|
21
|
-
isQueuingNotify = true;
|
|
22
|
-
queueMicrotask(() => {
|
|
23
|
-
isQueuingNotify = false;
|
|
24
|
-
const queue = notifyQueue;
|
|
25
|
-
notifyQueue = [];
|
|
26
|
-
queue.forEach((cb) => cb());
|
|
27
|
-
});
|
|
28
|
-
}
|
|
17
|
+
currentSubscribers.forEach((cb) => cb());
|
|
29
18
|
}
|
|
30
19
|
}
|
|
31
20
|
export class Observer {
|