@cleanweb/oore 1.0.0
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 +374 -0
- package/README.old.md +342 -0
- package/build/base/index.d.ts +3 -0
- package/build/base/index.js +19 -0
- package/build/base/merged-state.d.ts +20 -0
- package/build/base/merged-state.js +84 -0
- package/build/base/methods.d.ts +59 -0
- package/build/base/methods.js +100 -0
- package/build/base/state/class-types.d.ts +20 -0
- package/build/base/state/class-types.js +2 -0
- package/build/base/state/class.d.ts +16 -0
- package/build/base/state/class.js +100 -0
- package/build/base/state/hook-types.d.ts +32 -0
- package/build/base/state/hook-types.js +2 -0
- package/build/base/state/hooks.d.ts +12 -0
- package/build/base/state/hooks.js +45 -0
- package/build/base/state/index.d.ts +8 -0
- package/build/base/state/index.js +35 -0
- package/build/classy/class/index.d.ts +115 -0
- package/build/classy/class/index.js +161 -0
- package/build/classy/class/types/extractor.d.ts +5 -0
- package/build/classy/class/types/extractor.js +2 -0
- package/build/classy/class/utils/function-name.d.ts +2 -0
- package/build/classy/class/utils/function-name.js +17 -0
- package/build/classy/index.d.ts +3 -0
- package/build/classy/index.js +19 -0
- package/build/classy/instance/index.d.ts +144 -0
- package/build/classy/instance/index.js +202 -0
- package/build/classy/instance/mount-callbacks.d.ts +5 -0
- package/build/classy/instance/mount-callbacks.js +30 -0
- package/build/classy/instance/types/hook.d.ts +13 -0
- package/build/classy/instance/types/hook.js +2 -0
- package/build/classy/logic/index.d.ts +116 -0
- package/build/classy/logic/index.js +128 -0
- package/build/classy/logic/types/hook.d.ts +16 -0
- package/build/classy/logic/types/hook.js +2 -0
- package/build/docs-src/api/base-classes.d.ts +3 -0
- package/build/docs-src/api/base-classes.js +9 -0
- package/build/docs-src/api/index.d.ts +13 -0
- package/build/docs-src/api/index.js +44 -0
- package/build/docs-src/api/references.d.ts +5 -0
- package/build/docs-src/api/references.js +31 -0
- package/build/globals.d.ts +84 -0
- package/build/globals.js +4 -0
- package/build/helpers/index.d.ts +13 -0
- package/build/helpers/index.js +31 -0
- package/build/helpers/mount-state.d.ts +5 -0
- package/build/helpers/mount-state.js +25 -0
- package/build/helpers/rerender.d.ts +5 -0
- package/build/helpers/rerender.js +29 -0
- package/build/helpers/type-guards.d.ts +1 -0
- package/build/helpers/type-guards.js +8 -0
- package/build/helpers/use-component/index.d.ts +6 -0
- package/build/helpers/use-component/index.js +17 -0
- package/build/helpers/use-component/types.d.ts +22 -0
- package/build/helpers/use-component/types.js +2 -0
- package/build/index.d.ts +4 -0
- package/build/index.js +19 -0
- package/build/tsconfig.json +49 -0
- package/package.json +87 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { UseInstance } from './types/hook';
|
|
2
|
+
import type { TPropsBase } from '../../classy/logic';
|
|
3
|
+
import { ComponentLogic } from '../../classy/logic';
|
|
4
|
+
type AsyncAllowedEffectCallback = () => Awaitable<IVoidFunction>;
|
|
5
|
+
/**
|
|
6
|
+
* A superset of {@link ComponentLogic} that adds support for lifecycle methods.
|
|
7
|
+
* This provides a declarative API for working with your React function component's lifecycle,
|
|
8
|
+
* a simpler alternative to the imperative approach with `useEffect` and/or `useMemo`.
|
|
9
|
+
*
|
|
10
|
+
* @see https://github.com/cleanjsweb/neat-react#lifecycle-useinstance
|
|
11
|
+
*/
|
|
12
|
+
export declare class ComponentInstance<TProps extends TPropsBase = null> extends ComponentLogic<TProps> {
|
|
13
|
+
/**
|
|
14
|
+
* Runs only _before_ the first render,
|
|
15
|
+
* i.e before the component instance is mounted.
|
|
16
|
+
*
|
|
17
|
+
* It is ignored on subsequent rerenders.
|
|
18
|
+
* Uses `useMemo()` under the hood.
|
|
19
|
+
*
|
|
20
|
+
* PS: You can conditionally update state from here, but with certain caveats.
|
|
21
|
+
* {@link https://react.dev/reference/react/useState#storing-information-from-previous-renders | See the React docs for details}.
|
|
22
|
+
*/
|
|
23
|
+
beforeMount: IVoidFunction;
|
|
24
|
+
/**
|
|
25
|
+
* Runs only **_after_** the first render, i.e after the component instance is mounted.
|
|
26
|
+
* It is ignored on subsequent rerenders.
|
|
27
|
+
*
|
|
28
|
+
* Should usually only be used for logic that does not directly take part in determining what to render, like
|
|
29
|
+
* logging and analytics.
|
|
30
|
+
*
|
|
31
|
+
* @returns A cleanup function.
|
|
32
|
+
*
|
|
33
|
+
* Uses `useEffect()` under the hood.
|
|
34
|
+
*/
|
|
35
|
+
onMount: AsyncAllowedEffectCallback;
|
|
36
|
+
/**
|
|
37
|
+
* Stores the object returned by {@link beforeRender}.
|
|
38
|
+
* @see {@link templateContext}
|
|
39
|
+
*/
|
|
40
|
+
private _templateContext;
|
|
41
|
+
/**
|
|
42
|
+
* Exposes the object returned by {@link beforeRender}.
|
|
43
|
+
*
|
|
44
|
+
* This is useful when you need to render some state or props
|
|
45
|
+
* in a transformed format. Put the transformation logic
|
|
46
|
+
* in {@link beforeRender} to the keep the main
|
|
47
|
+
* function component body clean.
|
|
48
|
+
*
|
|
49
|
+
* ******
|
|
50
|
+
*
|
|
51
|
+
* @example <caption>Using `templateContext`.</caption>
|
|
52
|
+
*
|
|
53
|
+
* ```tsx
|
|
54
|
+
* class MyComponentLogic extends ComponentInstance {
|
|
55
|
+
* beforeRender = () => {
|
|
56
|
+
* const title = `My Site | ${this.props.title}`;
|
|
57
|
+
* return { title };
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* const MyComponent = (props) => {
|
|
61
|
+
* const self = useInstance(MyComponentLogic, props);
|
|
62
|
+
* const { templateContext: ctx, state } = self;
|
|
63
|
+
*
|
|
64
|
+
* return (
|
|
65
|
+
* <h1>
|
|
66
|
+
* {ctx.title}
|
|
67
|
+
* </h1>
|
|
68
|
+
* <p>{props.description}</p>
|
|
69
|
+
* );
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
get templateContext(): ReturnType<this["beforeRender"]>;
|
|
74
|
+
/**
|
|
75
|
+
* Runs _before_ every render cycle, including the first.
|
|
76
|
+
* Useful for logic that is involved in determining what to render.
|
|
77
|
+
*
|
|
78
|
+
* It runs in the same way as logic placed directly into the
|
|
79
|
+
* function component body preceding the return statement.
|
|
80
|
+
*
|
|
81
|
+
* This is the ideal place to transform data for display.
|
|
82
|
+
* Return the transformed data in an object, and the object will
|
|
83
|
+
* availble as [`self.templateContext`]({@link templateContext})
|
|
84
|
+
* for use in your JSX template.
|
|
85
|
+
*
|
|
86
|
+
* PS: You can conditionally update state from here, but with certain caveats.
|
|
87
|
+
* {@link https://react.dev/reference/react/useState#storing-information-from-previous-renders | See the React docs for details}.
|
|
88
|
+
*/
|
|
89
|
+
beforeRender: () => object | void;
|
|
90
|
+
/**
|
|
91
|
+
* Runs **_after_** every render cycle, including the first.
|
|
92
|
+
*
|
|
93
|
+
* Should usually only be used for logic that does not directly take part in determining what to render,
|
|
94
|
+
* like logging and analytics.
|
|
95
|
+
*
|
|
96
|
+
* Uses `useEffect()` under the hood.
|
|
97
|
+
*
|
|
98
|
+
* @returns A cleanup function.
|
|
99
|
+
*/
|
|
100
|
+
onRender: AsyncAllowedEffectCallback;
|
|
101
|
+
/**
|
|
102
|
+
* Runs when the component is unmounted.
|
|
103
|
+
* It is called _after_ the cleanup function returned by onMount.
|
|
104
|
+
*/
|
|
105
|
+
cleanUp: IVoidFunction;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Enables full separation of concerns between a React component's template
|
|
109
|
+
* and all of the logic that drives it.
|
|
110
|
+
*
|
|
111
|
+
* Returns an object that fully represents a logical instance
|
|
112
|
+
* of the rendered React component, with the exception of the JSX template itself.
|
|
113
|
+
*
|
|
114
|
+
* This means that all of your component's logic and lifecycle handlers
|
|
115
|
+
* can be externalized from the function component itself,
|
|
116
|
+
* and defined in a separate class.
|
|
117
|
+
*
|
|
118
|
+
* The provided class should be a subclass of {@link ComponentInstance}.
|
|
119
|
+
*/
|
|
120
|
+
export declare const useInstance: UseInstance;
|
|
121
|
+
export {};
|
|
122
|
+
/** /
|
|
123
|
+
testing: {
|
|
124
|
+
class A extends ComponentInstance<EmptyObject> {
|
|
125
|
+
getInitialState = (p?: object) => ({putan: ''});
|
|
126
|
+
// k = this.props.o
|
|
127
|
+
am = this.state['_initialValues_'];
|
|
128
|
+
k = this.am.putan;
|
|
129
|
+
|
|
130
|
+
beforeRender = () => ({g: ''});
|
|
131
|
+
|
|
132
|
+
useHooks = () => {
|
|
133
|
+
return {j: 9};
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const a = useInstance(A, {});
|
|
138
|
+
a.am;
|
|
139
|
+
|
|
140
|
+
// a.props['o'];
|
|
141
|
+
type bbbb = A['state'];
|
|
142
|
+
type ttt = bbbb['put'];
|
|
143
|
+
}
|
|
144
|
+
/**/
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __extends = (this && this.__extends) || (function () {
|
|
3
|
+
var extendStatics = function (d, b) {
|
|
4
|
+
extendStatics = Object.setPrototypeOf ||
|
|
5
|
+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
|
+
return extendStatics(d, b);
|
|
8
|
+
};
|
|
9
|
+
return function (d, b) {
|
|
10
|
+
if (typeof b !== "function" && b !== null)
|
|
11
|
+
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
12
|
+
extendStatics(d, b);
|
|
13
|
+
function __() { this.constructor = d; }
|
|
14
|
+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
15
|
+
};
|
|
16
|
+
})();
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.useInstance = exports.ComponentInstance = void 0;
|
|
19
|
+
var react_1 = require("react");
|
|
20
|
+
var logic_1 = require("../../classy/logic");
|
|
21
|
+
var mount_callbacks_1 = require("./mount-callbacks");
|
|
22
|
+
var helpers_1 = require("../../helpers");
|
|
23
|
+
/**
|
|
24
|
+
* A superset of {@link ComponentLogic} that adds support for lifecycle methods.
|
|
25
|
+
* This provides a declarative API for working with your React function component's lifecycle,
|
|
26
|
+
* a simpler alternative to the imperative approach with `useEffect` and/or `useMemo`.
|
|
27
|
+
*
|
|
28
|
+
* @see https://github.com/cleanjsweb/neat-react#lifecycle-useinstance
|
|
29
|
+
*/
|
|
30
|
+
var ComponentInstance = /** @class */ (function (_super) {
|
|
31
|
+
__extends(ComponentInstance, _super);
|
|
32
|
+
function ComponentInstance() {
|
|
33
|
+
var _this = _super !== null && _super.apply(this, arguments) || this;
|
|
34
|
+
/**
|
|
35
|
+
* Runs only _before_ the first render,
|
|
36
|
+
* i.e before the component instance is mounted.
|
|
37
|
+
*
|
|
38
|
+
* It is ignored on subsequent rerenders.
|
|
39
|
+
* Uses `useMemo()` under the hood.
|
|
40
|
+
*
|
|
41
|
+
* PS: You can conditionally update state from here, but with certain caveats.
|
|
42
|
+
* {@link https://react.dev/reference/react/useState#storing-information-from-previous-renders | See the React docs for details}.
|
|
43
|
+
*/
|
|
44
|
+
_this.beforeMount = function () { };
|
|
45
|
+
/**
|
|
46
|
+
* Runs only **_after_** the first render, i.e after the component instance is mounted.
|
|
47
|
+
* It is ignored on subsequent rerenders.
|
|
48
|
+
*
|
|
49
|
+
* Should usually only be used for logic that does not directly take part in determining what to render, like
|
|
50
|
+
* logging and analytics.
|
|
51
|
+
*
|
|
52
|
+
* @returns A cleanup function.
|
|
53
|
+
*
|
|
54
|
+
* Uses `useEffect()` under the hood.
|
|
55
|
+
*/
|
|
56
|
+
_this.onMount = function () { return helpers_1.noOp; };
|
|
57
|
+
/**
|
|
58
|
+
* Runs _before_ every render cycle, including the first.
|
|
59
|
+
* Useful for logic that is involved in determining what to render.
|
|
60
|
+
*
|
|
61
|
+
* It runs in the same way as logic placed directly into the
|
|
62
|
+
* function component body preceding the return statement.
|
|
63
|
+
*
|
|
64
|
+
* This is the ideal place to transform data for display.
|
|
65
|
+
* Return the transformed data in an object, and the object will
|
|
66
|
+
* availble as [`self.templateContext`]({@link templateContext})
|
|
67
|
+
* for use in your JSX template.
|
|
68
|
+
*
|
|
69
|
+
* PS: You can conditionally update state from here, but with certain caveats.
|
|
70
|
+
* {@link https://react.dev/reference/react/useState#storing-information-from-previous-renders | See the React docs for details}.
|
|
71
|
+
*/
|
|
72
|
+
_this.beforeRender = function () { };
|
|
73
|
+
/**
|
|
74
|
+
* Runs **_after_** every render cycle, including the first.
|
|
75
|
+
*
|
|
76
|
+
* Should usually only be used for logic that does not directly take part in determining what to render,
|
|
77
|
+
* like logging and analytics.
|
|
78
|
+
*
|
|
79
|
+
* Uses `useEffect()` under the hood.
|
|
80
|
+
*
|
|
81
|
+
* @returns A cleanup function.
|
|
82
|
+
*/
|
|
83
|
+
_this.onRender = function () { return helpers_1.noOp; };
|
|
84
|
+
/**
|
|
85
|
+
* Runs when the component is unmounted.
|
|
86
|
+
* It is called _after_ the cleanup function returned by onMount.
|
|
87
|
+
*/
|
|
88
|
+
_this.cleanUp = function () { };
|
|
89
|
+
return _this;
|
|
90
|
+
}
|
|
91
|
+
Object.defineProperty(ComponentInstance.prototype, "templateContext", {
|
|
92
|
+
/**
|
|
93
|
+
* Exposes the object returned by {@link beforeRender}.
|
|
94
|
+
*
|
|
95
|
+
* This is useful when you need to render some state or props
|
|
96
|
+
* in a transformed format. Put the transformation logic
|
|
97
|
+
* in {@link beforeRender} to the keep the main
|
|
98
|
+
* function component body clean.
|
|
99
|
+
*
|
|
100
|
+
* ******
|
|
101
|
+
*
|
|
102
|
+
* @example <caption>Using `templateContext`.</caption>
|
|
103
|
+
*
|
|
104
|
+
* ```tsx
|
|
105
|
+
* class MyComponentLogic extends ComponentInstance {
|
|
106
|
+
* beforeRender = () => {
|
|
107
|
+
* const title = `My Site | ${this.props.title}`;
|
|
108
|
+
* return { title };
|
|
109
|
+
* }
|
|
110
|
+
* }
|
|
111
|
+
* const MyComponent = (props) => {
|
|
112
|
+
* const self = useInstance(MyComponentLogic, props);
|
|
113
|
+
* const { templateContext: ctx, state } = self;
|
|
114
|
+
*
|
|
115
|
+
* return (
|
|
116
|
+
* <h1>
|
|
117
|
+
* {ctx.title}
|
|
118
|
+
* </h1>
|
|
119
|
+
* <p>{props.description}</p>
|
|
120
|
+
* );
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
get: function () {
|
|
125
|
+
return this._templateContext;
|
|
126
|
+
},
|
|
127
|
+
enumerable: false,
|
|
128
|
+
configurable: true
|
|
129
|
+
});
|
|
130
|
+
return ComponentInstance;
|
|
131
|
+
}(logic_1.ComponentLogic));
|
|
132
|
+
exports.ComponentInstance = ComponentInstance;
|
|
133
|
+
;
|
|
134
|
+
/**
|
|
135
|
+
* Enables full separation of concerns between a React component's template
|
|
136
|
+
* and all of the logic that drives it.
|
|
137
|
+
*
|
|
138
|
+
* Returns an object that fully represents a logical instance
|
|
139
|
+
* of the rendered React component, with the exception of the JSX template itself.
|
|
140
|
+
*
|
|
141
|
+
* This means that all of your component's logic and lifecycle handlers
|
|
142
|
+
* can be externalized from the function component itself,
|
|
143
|
+
* and defined in a separate class.
|
|
144
|
+
*
|
|
145
|
+
* The provided class should be a subclass of {@link ComponentInstance}.
|
|
146
|
+
*/
|
|
147
|
+
var useInstance = function () {
|
|
148
|
+
var _a;
|
|
149
|
+
var args = [];
|
|
150
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
151
|
+
args[_i] = arguments[_i];
|
|
152
|
+
}
|
|
153
|
+
var Component = args[0], _b = args[1], props = _b === void 0 ? {} : _b;
|
|
154
|
+
// useHooks.
|
|
155
|
+
var instance = (0, logic_1.useLogic)(Component, props);
|
|
156
|
+
// beforeMount, onMount, cleanUp.
|
|
157
|
+
(0, mount_callbacks_1.useMountCallbacks)(instance);
|
|
158
|
+
// beforeRender.
|
|
159
|
+
/**
|
|
160
|
+
* A proxy variable to allow typechecking of the assignment
|
|
161
|
+
* to `self.templateContext` despite the need for "readonly" error suppression.
|
|
162
|
+
*/
|
|
163
|
+
var _templateContextProxy_;
|
|
164
|
+
// @ts-expect-error Assigning to a readonly property.
|
|
165
|
+
instance._templateContext = (_templateContextProxy_ = (_a = instance.beforeRender) === null || _a === void 0 ? void 0 : _a.call(instance));
|
|
166
|
+
// onRender.
|
|
167
|
+
(0, react_1.useEffect)(function () {
|
|
168
|
+
var _a;
|
|
169
|
+
var cleanupAfterRerender = (_a = instance.onRender) === null || _a === void 0 ? void 0 : _a.call(instance);
|
|
170
|
+
return function () {
|
|
171
|
+
if (typeof cleanupAfterRerender === 'function')
|
|
172
|
+
cleanupAfterRerender();
|
|
173
|
+
else
|
|
174
|
+
cleanupAfterRerender === null || cleanupAfterRerender === void 0 ? void 0 : cleanupAfterRerender.then(function (cleanUp) { return cleanUp === null || cleanUp === void 0 ? void 0 : cleanUp(); });
|
|
175
|
+
};
|
|
176
|
+
});
|
|
177
|
+
return instance;
|
|
178
|
+
};
|
|
179
|
+
exports.useInstance = useInstance;
|
|
180
|
+
/** /
|
|
181
|
+
testing: {
|
|
182
|
+
class A extends ComponentInstance<EmptyObject> {
|
|
183
|
+
getInitialState = (p?: object) => ({putan: ''});
|
|
184
|
+
// k = this.props.o
|
|
185
|
+
am = this.state['_initialValues_'];
|
|
186
|
+
k = this.am.putan;
|
|
187
|
+
|
|
188
|
+
beforeRender = () => ({g: ''});
|
|
189
|
+
|
|
190
|
+
useHooks = () => {
|
|
191
|
+
return {j: 9};
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const a = useInstance(A, {});
|
|
196
|
+
a.am;
|
|
197
|
+
|
|
198
|
+
// a.props['o'];
|
|
199
|
+
type bbbb = A['state'];
|
|
200
|
+
type ttt = bbbb['put'];
|
|
201
|
+
}
|
|
202
|
+
/**/
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useMountCallbacks = void 0;
|
|
4
|
+
var react_1 = require("react");
|
|
5
|
+
var mount_state_1 = require("../../helpers/mount-state");
|
|
6
|
+
/** @internal */
|
|
7
|
+
var useMountCallbacks = function (instance) {
|
|
8
|
+
var _a;
|
|
9
|
+
var isMounted = (0, mount_state_1.useMountState)();
|
|
10
|
+
if (!isMounted())
|
|
11
|
+
(_a = instance.beforeMount) === null || _a === void 0 ? void 0 : _a.call(instance);
|
|
12
|
+
(0, react_1.useEffect)(function () {
|
|
13
|
+
var _a;
|
|
14
|
+
var mountHandlerCleanUp = (_a = instance.onMount) === null || _a === void 0 ? void 0 : _a.call(instance);
|
|
15
|
+
return function () {
|
|
16
|
+
var doCleanUp = function (runMountCleaners) {
|
|
17
|
+
var _a;
|
|
18
|
+
runMountCleaners === null || runMountCleaners === void 0 ? void 0 : runMountCleaners();
|
|
19
|
+
(_a = instance.cleanUp) === null || _a === void 0 ? void 0 : _a.call(instance);
|
|
20
|
+
};
|
|
21
|
+
if (typeof mountHandlerCleanUp === 'function') {
|
|
22
|
+
doCleanUp(mountHandlerCleanUp);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
mountHandlerCleanUp === null || mountHandlerCleanUp === void 0 ? void 0 : mountHandlerCleanUp.then(doCleanUp);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}, []);
|
|
29
|
+
};
|
|
30
|
+
exports.useMountCallbacks = useMountCallbacks;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ComponentInstance } from '..';
|
|
2
|
+
type UIClassParam = typeof ComponentInstance<NonNullable<any>>;
|
|
3
|
+
type UIProplessClassParam = typeof ComponentInstance<null>;
|
|
4
|
+
export type UseInstance = {
|
|
5
|
+
<Class extends UIProplessClassParam>(Methods: Class): InstanceType<Class>;
|
|
6
|
+
<Class extends UIClassParam>(Methods: Class, props: InstanceType<Class>['props']): InstanceType<Class>;
|
|
7
|
+
};
|
|
8
|
+
export type UIParams = [
|
|
9
|
+
Class: typeof ComponentInstance<any>,
|
|
10
|
+
props?: object
|
|
11
|
+
];
|
|
12
|
+
export type UIReturn = ComponentInstance<any>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import type { TCleanState } from '../../base/state';
|
|
2
|
+
import type { UseLogic } from './types/hook';
|
|
3
|
+
/**
|
|
4
|
+
* The base type for the `props` type argument.
|
|
5
|
+
*
|
|
6
|
+
* This is not the type of the `props` property itself.
|
|
7
|
+
* It merely defines the type constraint for the type argument
|
|
8
|
+
* passed when extending any of the logic classes.
|
|
9
|
+
*
|
|
10
|
+
* It accepts null for components that don't take any props.
|
|
11
|
+
*/
|
|
12
|
+
export type TPropsBase = NonPrimitive | null;
|
|
13
|
+
/**
|
|
14
|
+
* Base class for a class that holds methods to be used in a function component.
|
|
15
|
+
*
|
|
16
|
+
* These methods will always have access to the latest copy of
|
|
17
|
+
* the component's state and props via `this.state` and `this.props` respectively.
|
|
18
|
+
*
|
|
19
|
+
* The special {@link Class['useHooks'] | useHooks} method allows you to consume
|
|
20
|
+
* React hooks within this class.
|
|
21
|
+
*
|
|
22
|
+
* Call the {@link useLogic} hook inside your function component to instantiate the class.
|
|
23
|
+
*
|
|
24
|
+
* @typeParam TProps - {@include ./types/tprops.md}
|
|
25
|
+
*/
|
|
26
|
+
export declare class ComponentLogic<TProps extends TPropsBase = null> {
|
|
27
|
+
/**
|
|
28
|
+
* A {@link TCleanState | `CleanState`} object.
|
|
29
|
+
* Holds all of your component's state,
|
|
30
|
+
* and methods for conveniently manipulating those values.
|
|
31
|
+
* Initialiazed with the object returned from your `getInitialState` method.
|
|
32
|
+
*/
|
|
33
|
+
readonly state: TCleanState<ReturnType<this['getInitialState']>>;
|
|
34
|
+
/** The props passed into your component at the time of rendering. */
|
|
35
|
+
readonly props: TProps extends null ? EmptyObject : TProps;
|
|
36
|
+
/**
|
|
37
|
+
* Values received from the hooks your component consumes.
|
|
38
|
+
* This holds the latest copy of the object returned by
|
|
39
|
+
* {@link useHooks}.
|
|
40
|
+
*
|
|
41
|
+
* You should not mutate this object directly.
|
|
42
|
+
* `useHooks` is called on every render and the object it returns
|
|
43
|
+
* will completely overwrite whatever the previous value of `this.hooks` was.
|
|
44
|
+
* `this.hooks` should only be updated through the object returned from `useHooks`.
|
|
45
|
+
*
|
|
46
|
+
* If you need to update a value from outside `useHooks` as well, then consider
|
|
47
|
+
* wrapping it with {@link React.useRef}, since React refs are persisted across rerenders.
|
|
48
|
+
*/
|
|
49
|
+
readonly hooks: ReturnType<this['useHooks']>;
|
|
50
|
+
/**
|
|
51
|
+
* Called before each instance of your component is mounted.
|
|
52
|
+
* It receives the initial `props` object and should return
|
|
53
|
+
* an object with the initial values for your component's state.
|
|
54
|
+
*/
|
|
55
|
+
getInitialState: (props?: TProps extends null ? undefined : TProps) => object;
|
|
56
|
+
/**
|
|
57
|
+
* Call React hooks from here. If your component needs
|
|
58
|
+
* access to values return from the hooks you call,
|
|
59
|
+
* expose those values by returning an object with said values.
|
|
60
|
+
*
|
|
61
|
+
* The returned object will be accessible as {@link hooks | `this.hooks`} within
|
|
62
|
+
* your component class.
|
|
63
|
+
*/
|
|
64
|
+
useHooks: () => object | void;
|
|
65
|
+
/**
|
|
66
|
+
* Persist class members during HMR. {@include ./hrm-preserve-keys.md}
|
|
67
|
+
* @privateRemarks
|
|
68
|
+
* @see {@link https://cleanjsweb.github.io/neat-react/classes/API.BaseClasses.ComponentLogic.html#_hmrpreservekeys | Full details}
|
|
69
|
+
*/
|
|
70
|
+
_hmrPreserveKeys: Array<keyof this | (string & {})>;
|
|
71
|
+
/**
|
|
72
|
+
* Run custom logic after HMR update. {@include ./on-hrm-update.md}
|
|
73
|
+
* @privateRemarks
|
|
74
|
+
* @see {@link https://cleanjsweb.github.io/neat-react/classes/API.BaseClasses.ComponentLogic.html#_onhmrupdate | Full details}
|
|
75
|
+
*/
|
|
76
|
+
_onHmrUpdate?: <TInstance extends this>(oldInstance: TInstance) => void;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns an instance of the provided class, which holds methods for your component and
|
|
80
|
+
* encapsulates hook calls with the special {@link ComponentLogic.useHooks | `useHooks`} method.
|
|
81
|
+
*
|
|
82
|
+
* The class argument must be a subclass of {@link ComponentLogic}.
|
|
83
|
+
*
|
|
84
|
+
* @see https://cleanjsweb.github.io/neat-react/functions/API.useLogic.html
|
|
85
|
+
*/
|
|
86
|
+
export declare const useLogic: UseLogic;
|
|
87
|
+
/** /
|
|
88
|
+
testing: {
|
|
89
|
+
const a: object = {b: ''};
|
|
90
|
+
|
|
91
|
+
type t = keyof typeof a;
|
|
92
|
+
|
|
93
|
+
class MyComponentLogic extends ComponentLogic<{}> {
|
|
94
|
+
getInitialState = () => ({b: 7});
|
|
95
|
+
b = () => 8 + this.state.b;
|
|
96
|
+
|
|
97
|
+
useHooks = () => ({a: 'undefined'});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
type tt = keyof {};
|
|
101
|
+
|
|
102
|
+
const self = useLogic(MyComponentLogic);
|
|
103
|
+
self.hooks;
|
|
104
|
+
self.useHooks();
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
const A = class C extends ComponentLogic {
|
|
108
|
+
getInitialState = () => ({a: 'l'});
|
|
109
|
+
a = () => this.state.a = '';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// const oa = {['a' as unknown as symbol]: 'boo'};
|
|
113
|
+
const oa = {['a']: 'boo'};
|
|
114
|
+
useLogic(A, oa);
|
|
115
|
+
}
|
|
116
|
+
/**/
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useLogic = exports.ComponentLogic = void 0;
|
|
4
|
+
var react_1 = require("react");
|
|
5
|
+
var state_1 = require("../../base/state");
|
|
6
|
+
/**
|
|
7
|
+
* Base class for a class that holds methods to be used in a function component.
|
|
8
|
+
*
|
|
9
|
+
* These methods will always have access to the latest copy of
|
|
10
|
+
* the component's state and props via `this.state` and `this.props` respectively.
|
|
11
|
+
*
|
|
12
|
+
* The special {@link Class['useHooks'] | useHooks} method allows you to consume
|
|
13
|
+
* React hooks within this class.
|
|
14
|
+
*
|
|
15
|
+
* Call the {@link useLogic} hook inside your function component to instantiate the class.
|
|
16
|
+
*
|
|
17
|
+
* @typeParam TProps - {@include ./types/tprops.md}
|
|
18
|
+
*/
|
|
19
|
+
var ComponentLogic = /** @class */ (function () {
|
|
20
|
+
function ComponentLogic() {
|
|
21
|
+
/**
|
|
22
|
+
* Called before each instance of your component is mounted.
|
|
23
|
+
* It receives the initial `props` object and should return
|
|
24
|
+
* an object with the initial values for your component's state.
|
|
25
|
+
*/
|
|
26
|
+
this.getInitialState = function (props) { return ({}); };
|
|
27
|
+
/**
|
|
28
|
+
* Call React hooks from here. If your component needs
|
|
29
|
+
* access to values return from the hooks you call,
|
|
30
|
+
* expose those values by returning an object with said values.
|
|
31
|
+
*
|
|
32
|
+
* The returned object will be accessible as {@link hooks | `this.hooks`} within
|
|
33
|
+
* your component class.
|
|
34
|
+
*/
|
|
35
|
+
this.useHooks = function () { };
|
|
36
|
+
/**
|
|
37
|
+
* Persist class members during HMR. {@include ./hrm-preserve-keys.md}
|
|
38
|
+
* @privateRemarks
|
|
39
|
+
* @see {@link https://cleanjsweb.github.io/neat-react/classes/API.BaseClasses.ComponentLogic.html#_hmrpreservekeys | Full details}
|
|
40
|
+
*/
|
|
41
|
+
this._hmrPreserveKeys = [];
|
|
42
|
+
}
|
|
43
|
+
return ComponentLogic;
|
|
44
|
+
}());
|
|
45
|
+
exports.ComponentLogic = ComponentLogic;
|
|
46
|
+
;
|
|
47
|
+
/**
|
|
48
|
+
* Returns an instance of the provided class, which holds methods for your component and
|
|
49
|
+
* encapsulates hook calls with the special {@link ComponentLogic.useHooks | `useHooks`} method.
|
|
50
|
+
*
|
|
51
|
+
* The class argument must be a subclass of {@link ComponentLogic}.
|
|
52
|
+
*
|
|
53
|
+
* @see https://cleanjsweb.github.io/neat-react/functions/API.useLogic.html
|
|
54
|
+
*/
|
|
55
|
+
var useLogic = function () {
|
|
56
|
+
var _a;
|
|
57
|
+
var args = [];
|
|
58
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
59
|
+
args[_i] = arguments[_i];
|
|
60
|
+
}
|
|
61
|
+
var Logic = args[0], _b = args[1], props = _b === void 0 ? {} : _b;
|
|
62
|
+
// In production, we only use the latestInstance the first time, and it's ignored every other time.
|
|
63
|
+
// This means changing the class at runtime will have no effect in production.
|
|
64
|
+
// latestInstance is only extracted into a separate variable for use in dev mode during HMR.
|
|
65
|
+
var latestInstance = (0, react_1.useMemo)(function () { return new Logic(); }, [Logic]);
|
|
66
|
+
// const latestInstance = useMemo(() => new Logic(), []);
|
|
67
|
+
var instanceRef = (0, react_1.useRef)(latestInstance);
|
|
68
|
+
var refreshState = function () {
|
|
69
|
+
var _a;
|
|
70
|
+
// @ts-expect-error
|
|
71
|
+
instanceRef.current.props = props;
|
|
72
|
+
// @ts-expect-error
|
|
73
|
+
instanceRef.current.state = (0, state_1.useCleanState)(instanceRef.current.getInitialState, props);
|
|
74
|
+
// @ts-expect-error
|
|
75
|
+
instanceRef.current.hooks = (_a = instanceRef.current.useHooks()) !== null && _a !== void 0 ? _a : {};
|
|
76
|
+
};
|
|
77
|
+
if (process.env.NODE_ENV === 'development' && instanceRef.current !== latestInstance) {
|
|
78
|
+
var oldInstance_1 = instanceRef.current;
|
|
79
|
+
latestInstance._hmrPreserveKeys.forEach(function (_key) {
|
|
80
|
+
var key = _key;
|
|
81
|
+
// @ts-expect-error We're assigning to readonly properties. Also, Typescript doesn't know that the type of the left and right side will always match, due to the dynamic access.
|
|
82
|
+
latestInstance[key] = oldInstance_1[key];
|
|
83
|
+
});
|
|
84
|
+
Reflect.ownKeys(oldInstance_1).forEach(function (_key) {
|
|
85
|
+
var key = _key;
|
|
86
|
+
delete oldInstance_1[key];
|
|
87
|
+
});
|
|
88
|
+
Object.setPrototypeOf(oldInstance_1, latestInstance);
|
|
89
|
+
instanceRef.current = latestInstance;
|
|
90
|
+
refreshState();
|
|
91
|
+
(_a = latestInstance._onHmrUpdate) === null || _a === void 0 ? void 0 : _a.call(latestInstance, oldInstance_1);
|
|
92
|
+
}
|
|
93
|
+
else
|
|
94
|
+
refreshState();
|
|
95
|
+
return instanceRef.current;
|
|
96
|
+
;
|
|
97
|
+
};
|
|
98
|
+
exports.useLogic = useLogic;
|
|
99
|
+
/** /
|
|
100
|
+
testing: {
|
|
101
|
+
const a: object = {b: ''};
|
|
102
|
+
|
|
103
|
+
type t = keyof typeof a;
|
|
104
|
+
|
|
105
|
+
class MyComponentLogic extends ComponentLogic<{}> {
|
|
106
|
+
getInitialState = () => ({b: 7});
|
|
107
|
+
b = () => 8 + this.state.b;
|
|
108
|
+
|
|
109
|
+
useHooks = () => ({a: 'undefined'});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type tt = keyof {};
|
|
113
|
+
|
|
114
|
+
const self = useLogic(MyComponentLogic);
|
|
115
|
+
self.hooks;
|
|
116
|
+
self.useHooks();
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
const A = class C extends ComponentLogic {
|
|
120
|
+
getInitialState = () => ({a: 'l'});
|
|
121
|
+
a = () => this.state.a = '';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// const oa = {['a' as unknown as symbol]: 'boo'};
|
|
125
|
+
const oa = {['a']: 'boo'};
|
|
126
|
+
useLogic(A, oa);
|
|
127
|
+
}
|
|
128
|
+
/**/
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ComponentLogic } from '..';
|
|
2
|
+
/*************************************
|
|
3
|
+
* # Hooks *
|
|
4
|
+
**************************************/
|
|
5
|
+
type ULClassParam = typeof ComponentLogic<NonNullable<any>>;
|
|
6
|
+
type ULProplessClassParam = typeof ComponentLogic<null>;
|
|
7
|
+
export type UseLogic = {
|
|
8
|
+
<Class extends ULProplessClassParam>(Methods: Class): InstanceType<Class>;
|
|
9
|
+
<Class extends ULClassParam>(Methods: Class, props: InstanceType<Class>['props']): InstanceType<Class>;
|
|
10
|
+
};
|
|
11
|
+
export type ULParams = [
|
|
12
|
+
Class: typeof ComponentLogic<any>,
|
|
13
|
+
props?: object
|
|
14
|
+
];
|
|
15
|
+
export type ULReturn = ComponentLogic<any>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ComponentInstance = exports.ComponentLogic = exports.ComponentMethods = void 0;
|
|
4
|
+
var methods_1 = require("../../base/methods");
|
|
5
|
+
Object.defineProperty(exports, "ComponentMethods", { enumerable: true, get: function () { return methods_1.ComponentMethods; } });
|
|
6
|
+
var logic_1 = require("../../classy/logic");
|
|
7
|
+
Object.defineProperty(exports, "ComponentLogic", { enumerable: true, get: function () { return logic_1.ComponentLogic; } });
|
|
8
|
+
var instance_1 = require("../../classy/instance");
|
|
9
|
+
Object.defineProperty(exports, "ComponentInstance", { enumerable: true, get: function () { return instance_1.ComponentInstance; } });
|