@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.
Files changed (60) hide show
  1. package/README.md +374 -0
  2. package/README.old.md +342 -0
  3. package/build/base/index.d.ts +3 -0
  4. package/build/base/index.js +19 -0
  5. package/build/base/merged-state.d.ts +20 -0
  6. package/build/base/merged-state.js +84 -0
  7. package/build/base/methods.d.ts +59 -0
  8. package/build/base/methods.js +100 -0
  9. package/build/base/state/class-types.d.ts +20 -0
  10. package/build/base/state/class-types.js +2 -0
  11. package/build/base/state/class.d.ts +16 -0
  12. package/build/base/state/class.js +100 -0
  13. package/build/base/state/hook-types.d.ts +32 -0
  14. package/build/base/state/hook-types.js +2 -0
  15. package/build/base/state/hooks.d.ts +12 -0
  16. package/build/base/state/hooks.js +45 -0
  17. package/build/base/state/index.d.ts +8 -0
  18. package/build/base/state/index.js +35 -0
  19. package/build/classy/class/index.d.ts +115 -0
  20. package/build/classy/class/index.js +161 -0
  21. package/build/classy/class/types/extractor.d.ts +5 -0
  22. package/build/classy/class/types/extractor.js +2 -0
  23. package/build/classy/class/utils/function-name.d.ts +2 -0
  24. package/build/classy/class/utils/function-name.js +17 -0
  25. package/build/classy/index.d.ts +3 -0
  26. package/build/classy/index.js +19 -0
  27. package/build/classy/instance/index.d.ts +144 -0
  28. package/build/classy/instance/index.js +202 -0
  29. package/build/classy/instance/mount-callbacks.d.ts +5 -0
  30. package/build/classy/instance/mount-callbacks.js +30 -0
  31. package/build/classy/instance/types/hook.d.ts +13 -0
  32. package/build/classy/instance/types/hook.js +2 -0
  33. package/build/classy/logic/index.d.ts +116 -0
  34. package/build/classy/logic/index.js +128 -0
  35. package/build/classy/logic/types/hook.d.ts +16 -0
  36. package/build/classy/logic/types/hook.js +2 -0
  37. package/build/docs-src/api/base-classes.d.ts +3 -0
  38. package/build/docs-src/api/base-classes.js +9 -0
  39. package/build/docs-src/api/index.d.ts +13 -0
  40. package/build/docs-src/api/index.js +44 -0
  41. package/build/docs-src/api/references.d.ts +5 -0
  42. package/build/docs-src/api/references.js +31 -0
  43. package/build/globals.d.ts +84 -0
  44. package/build/globals.js +4 -0
  45. package/build/helpers/index.d.ts +13 -0
  46. package/build/helpers/index.js +31 -0
  47. package/build/helpers/mount-state.d.ts +5 -0
  48. package/build/helpers/mount-state.js +25 -0
  49. package/build/helpers/rerender.d.ts +5 -0
  50. package/build/helpers/rerender.js +29 -0
  51. package/build/helpers/type-guards.d.ts +1 -0
  52. package/build/helpers/type-guards.js +8 -0
  53. package/build/helpers/use-component/index.d.ts +6 -0
  54. package/build/helpers/use-component/index.js +17 -0
  55. package/build/helpers/use-component/types.d.ts +22 -0
  56. package/build/helpers/use-component/types.js +2 -0
  57. package/build/index.d.ts +4 -0
  58. package/build/index.js +19 -0
  59. package/build/tsconfig.json +49 -0
  60. 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,5 @@
1
+ import { ComponentInstance } from '.';
2
+ type UseMountCallbacks = <TInstance extends ComponentInstance>(instance: TInstance) => void;
3
+ /** @internal */
4
+ export declare const useMountCallbacks: UseMountCallbacks;
5
+ export {};
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export { ComponentMethods } from '../../base/methods';
2
+ export { ComponentLogic } from '../../classy/logic';
3
+ export { ComponentInstance } from '../../classy/instance';
@@ -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; } });