mvvm-mobx 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 +189 -0
- package/dist/commands/RelayCommand.d.ts +9 -0
- package/dist/commands/RelayCommand.js +68 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +18 -0
- package/dist/commands/prop-helpers.d.ts +5 -0
- package/dist/commands/prop-helpers.js +13 -0
- package/dist/core/CounterFlag.d.ts +15 -0
- package/dist/core/CounterFlag.js +152 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/interaction/CommonInteractionResponses.d.ts +24 -0
- package/dist/interaction/CommonInteractionResponses.js +13 -0
- package/dist/interaction/Interact.d.ts +49 -0
- package/dist/interaction/Interact.js +87 -0
- package/dist/interaction/InteractionManager.d.ts +5 -0
- package/dist/interaction/InteractionManager.js +4 -0
- package/dist/interaction/InteractionRequest.d.ts +14 -0
- package/dist/interaction/InteractionRequest.js +2 -0
- package/dist/interaction/InteractionResponse.d.ts +21 -0
- package/dist/interaction/InteractionResponse.js +21 -0
- package/dist/interaction/ReportInteractionOperationFinished.d.ts +3 -0
- package/dist/interaction/ReportInteractionOperationFinished.js +2 -0
- package/dist/interaction/SingleConcurrentInteractionManager.d.ts +9 -0
- package/dist/interaction/SingleConcurrentInteractionManager.js +78 -0
- package/dist/interaction/index.d.ts +8 -0
- package/dist/interaction/index.js +24 -0
- package/dist/interaction/wireUpSingleConcurrentInteractionHandler.d.ts +9 -0
- package/dist/interaction/wireUpSingleConcurrentInteractionHandler.js +45 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# MVVM MobX
|
|
2
|
+
|
|
3
|
+
This package contains classes and utils for working with MVVM over MobX.
|
|
4
|
+
It adheres to the concepts of Prism.
|
|
5
|
+
|
|
6
|
+
## CounterFlag
|
|
7
|
+
|
|
8
|
+
A flag that bases it true/false value on an active counter.
|
|
9
|
+
It has an internal counter that increases when an operation is starting and decreases when an operation is ending.
|
|
10
|
+
When the counter is 0, the flag is marked as inactive (`flag.isActive === false`). When the counter is greater than 0 then the flag is marked as active.
|
|
11
|
+
|
|
12
|
+
Operations are bounded with the `using` method. When this method starts it means that an operation has started and the counter increases. When this method ends it means that that operation has ended and the counter decreases.
|
|
13
|
+
You can call `using` many times simultaneously.
|
|
14
|
+
|
|
15
|
+
An example on how to use it:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
const flag = new CounterFlag();
|
|
19
|
+
|
|
20
|
+
await flag.using(async () => {
|
|
21
|
+
// At this point the counter has been increased and the flag is active.
|
|
22
|
+
|
|
23
|
+
await runSomeWork(); // Inside runSomeWork you can also call flag.using
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// At this point the counter has been decreased and the flag is inactive.
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## RelayCommand
|
|
30
|
+
|
|
31
|
+
Implements the [Command pattern](https://onewindowsdev.com/2016/06/16/the-command-pattern-and-mvvm/) by relaying the actual work to external functions that are provided in the constructor.
|
|
32
|
+
In Prism it was called DelegateCommand. This name was not used here because in JS the term "delegate" is not known.
|
|
33
|
+
|
|
34
|
+
You pass an execution method in the constructor (and possibly another method to check if an execution is allowed).
|
|
35
|
+
The UI should bind to the command (can use the helper `commandProps` function) and act appropriately.
|
|
36
|
+
`RelayCommand` works with MobX, so the `canExecute` method is observable is the function that is passed to the constructor is also observable.
|
|
37
|
+
Observable functions don't have to be decorated with `@computed` in order to work. See more info: https://mobx.js.org/computeds-with-args.html#1-derivations-dont-_need_-to-be-computed
|
|
38
|
+
|
|
39
|
+
Using a relay command:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
const saveProjectCommand = new RelayCommand(this.saveProject, this.canSaveProject);
|
|
43
|
+
|
|
44
|
+
private function async saveProject() {
|
|
45
|
+
// save project
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private function canSaveProject() {
|
|
49
|
+
return this.isProjectValid;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The command can support 1 parameter:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
const deleteProjectCommand = new DelegateCommand<string>(this.deleteProject, this.canDeleteProject);
|
|
57
|
+
|
|
58
|
+
private function async deleteProject(projectId: string) {
|
|
59
|
+
// save project
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
private function canDeleteProject(projectId: string) {
|
|
63
|
+
return !this.isReadOnly;
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
In the UI you can bind buttons to the commands:
|
|
69
|
+
|
|
70
|
+
```jsx
|
|
71
|
+
<Button {...bindCommand(vm.saveProjectCommand)}>
|
|
72
|
+
Save Project
|
|
73
|
+
</Button>
|
|
74
|
+
|
|
75
|
+
{vm.projects.map(project =>
|
|
76
|
+
observer(
|
|
77
|
+
<div>
|
|
78
|
+
{project.name}
|
|
79
|
+
<Button {...bindCommand(vm.deleteProjectCommand, project.id)}>
|
|
80
|
+
Delete Project
|
|
81
|
+
</Button>
|
|
82
|
+
</div>)
|
|
83
|
+
)}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
What it does in the "Save Project" button is equivalent to:
|
|
87
|
+
|
|
88
|
+
```jsx
|
|
89
|
+
<Button
|
|
90
|
+
onClick={() => vm.saveProjectCommand.execute()}
|
|
91
|
+
disabled={!vm.saveProjectCommand.canExecute()}>
|
|
92
|
+
|
|
93
|
+
Save Project
|
|
94
|
+
</Button>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The command has an indication while it's working (`command.workingFlag.isActive`)
|
|
98
|
+
|
|
99
|
+
## Interaction
|
|
100
|
+
|
|
101
|
+
Many times we need to communicate with the user and wait for his response. This is called **interaction**.
|
|
102
|
+
Common scenarios:
|
|
103
|
+
* The user wants to perform in irreversible action and we want to confirm that he's sure.
|
|
104
|
+
* The user wants to perform an action that has implications and we want to confirm.
|
|
105
|
+
* Presenting error messages.
|
|
106
|
+
|
|
107
|
+
Since according to MVVM all the above is done and decided by the business logic, it should reside in the view model.
|
|
108
|
+
|
|
109
|
+
So how do we interact with the user from the VM through the view, in the middle of a business logic flow?
|
|
110
|
+
|
|
111
|
+
This is why we have the Interaction Manager. We can create an `InteractionRequest` object and request an interaction from the user:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const interactionObject: InteractionRequest = {
|
|
115
|
+
title: 'Confirmation',
|
|
116
|
+
content: 'Are you sure you want to delete?',
|
|
117
|
+
actions: [
|
|
118
|
+
CommonInteractionActions.yes.action
|
|
119
|
+
CommonInteractionActions.no.action
|
|
120
|
+
]
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (await interactionManager.requestInteraction(interactionObject)) === CommonInteractionActions.yes.id) {
|
|
124
|
+
deleteItem();
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
**Note**
|
|
130
|
+
|
|
131
|
+
In the above example, you can use the shorthand:
|
|
132
|
+
```typescript
|
|
133
|
+
if (await Interact.withYesNo(interactionManager, 'Confirmation', 'Are you sure you want to delete?')) {
|
|
134
|
+
deleteItem();
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
In many cases you need some action with cancellation (like "Delete" and "Cancel" buttons).
|
|
138
|
+
You can use the shorthand:
|
|
139
|
+
```typescript
|
|
140
|
+
const deleteAction = new InteractionAction('delete', 'Delete');
|
|
141
|
+
|
|
142
|
+
// Show interaction view with "Delete" and "Cancel" buttons:
|
|
143
|
+
if (await Interact.withCustomAndCancel(interactionManager, 'Confirmation', 'Are you sure you want to delete?', deleteAction)) {
|
|
144
|
+
deleteItem();
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
The interaction object has title, content, and actions for the user to respond on the request.
|
|
151
|
+
|
|
152
|
+
We create this object as pass it to the manager.
|
|
153
|
+
In the View layer, there should be a component that listens to this manager and when interaction is requested, it should present it to the user, and pass the user's response back to the manager.
|
|
154
|
+
|
|
155
|
+
The view is responsible to present the interaction in a manner which it finds suitable. It can be a modal, a toast notification and so on.
|
|
156
|
+
|
|
157
|
+
Once the user chooses the response, the promise of the `requestInteraction` method should be finished.
|
|
158
|
+
|
|
159
|
+
### Finishing the interaction through code
|
|
160
|
+
If you implement the `ReportInteractionOperationFinished` interface, you can finish the interaction through code by calling the interface's `onOperationFinished` function.
|
|
161
|
+
|
|
162
|
+
This package includes a pre-implemented `SingleConcurrentInteractionManager` interaction manager for handling one interaction request at a time. When using modals, this should fit most of the use cases.
|
|
163
|
+
|
|
164
|
+
To implement the View side, there's also a `useSingleConcurrentInteractionHandler` hook that take case of most of the logic.
|
|
165
|
+
|
|
166
|
+
## PaneViewModelBase
|
|
167
|
+
|
|
168
|
+
Intended for view models that represent a pane in the app that handles data and actions.
|
|
169
|
+
|
|
170
|
+
When performing long actions (like setting/getting data from the server), you can put your logic inside the runLongWork method:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
await this.runLongWork(async () => {
|
|
174
|
+
await saveToServer();
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The method will:
|
|
179
|
+
1. Set the workingFlag.isActive property to `true` so that UI elements could present an in-progress state.
|
|
180
|
+
2. Catch exception (except AcknowledgementRequiredException) and put the message in the `error` property for the UI to bind to.
|
|
181
|
+
3. Log the exceptions.
|
|
182
|
+
|
|
183
|
+
You can use the helper `errorModalProps` function in the UI to automatically bind and handle the `error` property:
|
|
184
|
+
|
|
185
|
+
```jsx
|
|
186
|
+
<ErrorModal
|
|
187
|
+
{...errorModalProps(vm)}
|
|
188
|
+
/>
|
|
189
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { CounterFlag } from '../core/CounterFlag';
|
|
2
|
+
export declare class RelayCommand<T = void> {
|
|
3
|
+
private readonly executeFunc;
|
|
4
|
+
private readonly canExecuteFunc?;
|
|
5
|
+
readonly workingFlag: CounterFlag;
|
|
6
|
+
constructor(executeFunc: (parameter: T) => Promise<void> | void, canExecuteFunc?: ((parameter: T) => boolean) | undefined);
|
|
7
|
+
canExecute(parameter: T): boolean;
|
|
8
|
+
execute(parameter: T): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
11
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
12
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
13
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
14
|
+
var _, done = false;
|
|
15
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
16
|
+
var context = {};
|
|
17
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
18
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
19
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
20
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
21
|
+
if (kind === "accessor") {
|
|
22
|
+
if (result === void 0) continue;
|
|
23
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
24
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
25
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
26
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
27
|
+
}
|
|
28
|
+
else if (_ = accept(result)) {
|
|
29
|
+
if (kind === "field") initializers.unshift(_);
|
|
30
|
+
else descriptor[key] = _;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
|
+
done = true;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.RelayCommand = void 0;
|
|
38
|
+
const mobx_1 = require("mobx");
|
|
39
|
+
const CounterFlag_1 = require("../core/CounterFlag");
|
|
40
|
+
let RelayCommand = (() => {
|
|
41
|
+
var _a;
|
|
42
|
+
let _instanceExtraInitializers = [];
|
|
43
|
+
let _execute_decorators;
|
|
44
|
+
return _a = class RelayCommand {
|
|
45
|
+
constructor(executeFunc, canExecuteFunc) {
|
|
46
|
+
this.executeFunc = (__runInitializers(this, _instanceExtraInitializers), executeFunc);
|
|
47
|
+
this.canExecuteFunc = canExecuteFunc;
|
|
48
|
+
this.workingFlag = new CounterFlag_1.CounterFlag();
|
|
49
|
+
(0, mobx_1.makeObservable)(this);
|
|
50
|
+
}
|
|
51
|
+
canExecute(parameter) {
|
|
52
|
+
return this.canExecuteFunc?.(parameter) ?? true;
|
|
53
|
+
}
|
|
54
|
+
async execute(parameter) {
|
|
55
|
+
await this.workingFlag.using(async () => {
|
|
56
|
+
await this.executeFunc(parameter);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
(() => {
|
|
61
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
62
|
+
_execute_decorators = [mobx_1.action];
|
|
63
|
+
__esDecorate(_a, null, _execute_decorators, { kind: "method", name: "execute", static: false, private: false, access: { has: obj => "execute" in obj, get: obj => obj.execute }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
64
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
65
|
+
})(),
|
|
66
|
+
_a;
|
|
67
|
+
})();
|
|
68
|
+
exports.RelayCommand = RelayCommand;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./RelayCommand"), exports);
|
|
18
|
+
__exportStar(require("./prop-helpers"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bindCommand = bindCommand;
|
|
4
|
+
function bindCommand(command, parameter = null, disabledPropName = 'disabled') {
|
|
5
|
+
if (!command) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
const isDisabled = !command.canExecute(parameter);
|
|
9
|
+
return {
|
|
10
|
+
onClick: () => command.execute(parameter),
|
|
11
|
+
[disabledPropName]: isDisabled,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class CounterFlag {
|
|
2
|
+
private counter;
|
|
3
|
+
constructor();
|
|
4
|
+
/**
|
|
5
|
+
* Activates the flag while executing the provided function.
|
|
6
|
+
* @param fn The function to execute while the flag is active.
|
|
7
|
+
*/
|
|
8
|
+
using(fn: () => Promise<void>): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Increments the counter and returns a disposable object that decreases the counter when disposed.
|
|
11
|
+
* @returns A disposable object that decreases the counter when disposed.
|
|
12
|
+
*/
|
|
13
|
+
increment(): Disposable;
|
|
14
|
+
get isActive(): boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
11
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
12
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
13
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
14
|
+
var _, done = false;
|
|
15
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
16
|
+
var context = {};
|
|
17
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
18
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
19
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
20
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
21
|
+
if (kind === "accessor") {
|
|
22
|
+
if (result === void 0) continue;
|
|
23
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
24
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
25
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
26
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
27
|
+
}
|
|
28
|
+
else if (_ = accept(result)) {
|
|
29
|
+
if (kind === "field") initializers.unshift(_);
|
|
30
|
+
else descriptor[key] = _;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
|
+
done = true;
|
|
35
|
+
};
|
|
36
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
37
|
+
if (value !== null && value !== void 0) {
|
|
38
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
39
|
+
var dispose, inner;
|
|
40
|
+
if (async) {
|
|
41
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
42
|
+
dispose = value[Symbol.asyncDispose];
|
|
43
|
+
}
|
|
44
|
+
if (dispose === void 0) {
|
|
45
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
46
|
+
dispose = value[Symbol.dispose];
|
|
47
|
+
if (async) inner = dispose;
|
|
48
|
+
}
|
|
49
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
50
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
51
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
52
|
+
}
|
|
53
|
+
else if (async) {
|
|
54
|
+
env.stack.push({ async: true });
|
|
55
|
+
}
|
|
56
|
+
return value;
|
|
57
|
+
};
|
|
58
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
59
|
+
return function (env) {
|
|
60
|
+
function fail(e) {
|
|
61
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
62
|
+
env.hasError = true;
|
|
63
|
+
}
|
|
64
|
+
var r, s = 0;
|
|
65
|
+
function next() {
|
|
66
|
+
while (r = env.stack.pop()) {
|
|
67
|
+
try {
|
|
68
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
69
|
+
if (r.dispose) {
|
|
70
|
+
var result = r.dispose.call(r.value);
|
|
71
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
72
|
+
}
|
|
73
|
+
else s |= 1;
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
fail(e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
80
|
+
if (env.hasError) throw env.error;
|
|
81
|
+
}
|
|
82
|
+
return next();
|
|
83
|
+
};
|
|
84
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
85
|
+
var e = new Error(message);
|
|
86
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
87
|
+
});
|
|
88
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
89
|
+
exports.CounterFlag = void 0;
|
|
90
|
+
const mobx_1 = require("mobx");
|
|
91
|
+
let CounterFlag = (() => {
|
|
92
|
+
var _a;
|
|
93
|
+
let _instanceExtraInitializers = [];
|
|
94
|
+
let _counter_decorators;
|
|
95
|
+
let _counter_initializers = [];
|
|
96
|
+
let _counter_extraInitializers = [];
|
|
97
|
+
let _using_decorators;
|
|
98
|
+
let _increment_decorators;
|
|
99
|
+
let _get_isActive_decorators;
|
|
100
|
+
return _a = class CounterFlag {
|
|
101
|
+
constructor() {
|
|
102
|
+
this.counter = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _counter_initializers, 0));
|
|
103
|
+
__runInitializers(this, _counter_extraInitializers);
|
|
104
|
+
(0, mobx_1.makeObservable)(this);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Activates the flag while executing the provided function.
|
|
108
|
+
* @param fn The function to execute while the flag is active.
|
|
109
|
+
*/
|
|
110
|
+
async using(fn) {
|
|
111
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
112
|
+
try {
|
|
113
|
+
const _ = __addDisposableResource(env_1, this.increment(), false);
|
|
114
|
+
await fn();
|
|
115
|
+
}
|
|
116
|
+
catch (e_1) {
|
|
117
|
+
env_1.error = e_1;
|
|
118
|
+
env_1.hasError = true;
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
__disposeResources(env_1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Increments the counter and returns a disposable object that decreases the counter when disposed.
|
|
126
|
+
* @returns A disposable object that decreases the counter when disposed.
|
|
127
|
+
*/
|
|
128
|
+
increment() {
|
|
129
|
+
this.counter++;
|
|
130
|
+
return {
|
|
131
|
+
[Symbol.dispose]: () => (0, mobx_1.runInAction)(() => this.counter--),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
get isActive() {
|
|
135
|
+
return this.counter > 0;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
(() => {
|
|
139
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
140
|
+
_counter_decorators = [mobx_1.observable];
|
|
141
|
+
_using_decorators = [mobx_1.action];
|
|
142
|
+
_increment_decorators = [mobx_1.action];
|
|
143
|
+
_get_isActive_decorators = [mobx_1.computed];
|
|
144
|
+
__esDecorate(_a, null, _using_decorators, { kind: "method", name: "using", static: false, private: false, access: { has: obj => "using" in obj, get: obj => obj.using }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
145
|
+
__esDecorate(_a, null, _increment_decorators, { kind: "method", name: "increment", static: false, private: false, access: { has: obj => "increment" in obj, get: obj => obj.increment }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
146
|
+
__esDecorate(_a, null, _get_isActive_decorators, { kind: "getter", name: "isActive", static: false, private: false, access: { has: obj => "isActive" in obj, get: obj => obj.isActive }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
147
|
+
__esDecorate(null, null, _counter_decorators, { kind: "field", name: "counter", static: false, private: false, access: { has: obj => "counter" in obj, get: obj => obj.counter, set: (obj, value) => { obj.counter = value; } }, metadata: _metadata }, _counter_initializers, _counter_extraInitializers);
|
|
148
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
149
|
+
})(),
|
|
150
|
+
_a;
|
|
151
|
+
})();
|
|
152
|
+
exports.CounterFlag = CounterFlag;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './CounterFlag';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./CounterFlag"), exports);
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./core"), exports);
|
|
18
|
+
__exportStar(require("./commands"), exports);
|
|
19
|
+
__exportStar(require("./interaction"), exports);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { InteractionResponse } from "./InteractionResponse";
|
|
2
|
+
/** Contains predefined common responses and their ID. */
|
|
3
|
+
export declare class CommonInteractionResponses {
|
|
4
|
+
static get ok(): {
|
|
5
|
+
id: string;
|
|
6
|
+
action: InteractionResponse;
|
|
7
|
+
};
|
|
8
|
+
static get cancel(): {
|
|
9
|
+
id: string;
|
|
10
|
+
action: InteractionResponse;
|
|
11
|
+
};
|
|
12
|
+
static get close(): {
|
|
13
|
+
id: string;
|
|
14
|
+
action: InteractionResponse;
|
|
15
|
+
};
|
|
16
|
+
static get yes(): {
|
|
17
|
+
id: string;
|
|
18
|
+
action: InteractionResponse;
|
|
19
|
+
};
|
|
20
|
+
static get no(): {
|
|
21
|
+
id: string;
|
|
22
|
+
action: InteractionResponse;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CommonInteractionResponses = void 0;
|
|
4
|
+
const InteractionResponse_1 = require("./InteractionResponse");
|
|
5
|
+
/** Contains predefined common responses and their ID. */
|
|
6
|
+
class CommonInteractionResponses {
|
|
7
|
+
static get ok() { return { id: 'ok', action: new InteractionResponse_1.InteractionResponse('ok', 'OK') }; }
|
|
8
|
+
static get cancel() { return { id: 'cancel', action: new InteractionResponse_1.InteractionResponse('cancel', 'Cancel') }; }
|
|
9
|
+
static get close() { return { id: 'close', action: new InteractionResponse_1.InteractionResponse('close', 'Close') }; }
|
|
10
|
+
static get yes() { return { id: 'yes', action: new InteractionResponse_1.InteractionResponse('yes', 'Yes') }; }
|
|
11
|
+
static get no() { return { id: 'no', action: new InteractionResponse_1.InteractionResponse('no', 'No') }; }
|
|
12
|
+
}
|
|
13
|
+
exports.CommonInteractionResponses = CommonInteractionResponses;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { InteractionResponse } from "./InteractionResponse";
|
|
2
|
+
import { InteractionManager } from "./InteractionManager";
|
|
3
|
+
import { RelayCommand } from "../commands/RelayCommand";
|
|
4
|
+
import { ReportInteractionOperationFinished } from "./ReportInteractionOperationFinished";
|
|
5
|
+
/** A syntactic-sugar util for working with interactions. */
|
|
6
|
+
export declare class Interact {
|
|
7
|
+
/**
|
|
8
|
+
* Interact with the user with default OK and Cancel responses.
|
|
9
|
+
* @param interactionManager In interaction manager to use.
|
|
10
|
+
* @param title The title of the interaction.
|
|
11
|
+
* @param content The content of the interaction.
|
|
12
|
+
* @returns true if @constant CommonInteractionResponses.Ok.id was selected, or false if @constant CommonInteractionResponses.Cancel.id was selected.
|
|
13
|
+
*/
|
|
14
|
+
static withOkCancel(interactionManager: InteractionManager, title: string | any, content: string | any): Promise<boolean>;
|
|
15
|
+
/**
|
|
16
|
+
* Interact with the user with a default OK action.
|
|
17
|
+
* @param interactionManager In interaction manager to use.
|
|
18
|
+
* @param title The title of the interaction.
|
|
19
|
+
* @param content The content of the interaction.
|
|
20
|
+
* @returns Should always return true.
|
|
21
|
+
*/
|
|
22
|
+
static withOk(interactionManager: InteractionManager, title: string | any, content: string | any): Promise<boolean>;
|
|
23
|
+
/**
|
|
24
|
+
* Interact with the user with default OK and Cancel responses, and a command to execute when the OK action is activated.
|
|
25
|
+
* @param interactionManager In interaction manager to use.
|
|
26
|
+
* @param title The title of the interaction.
|
|
27
|
+
* @param content The content of the interaction.
|
|
28
|
+
* @param okCommand The comment to execute when the OK action is selected.
|
|
29
|
+
* @returns true if @constant CommonInteractionResponses.Ok.id was selected, or false if @constant CommonInteractionResponses.Cancel.id was selected.
|
|
30
|
+
*/
|
|
31
|
+
static withOkCommand(interactionManager: InteractionManager, title: string | any, content: ReportInteractionOperationFinished, okCommand: RelayCommand): Promise<boolean>;
|
|
32
|
+
/**
|
|
33
|
+
* Interact with the user with default Yes and No responses.
|
|
34
|
+
* @param interactionManager In interaction manager to use.
|
|
35
|
+
* @param title The title of the interaction.
|
|
36
|
+
* @param content The content of the interaction.
|
|
37
|
+
* @returns true if @constant CommonInteractionResponses.Yes.id was selected, or false if @constant CommonInteractionResponses.No.id was selected.
|
|
38
|
+
*/
|
|
39
|
+
static withYesNo(interactionManager: InteractionManager, title: string | any, content: string | any): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Interact with the user with a custom action and Cancel action.
|
|
42
|
+
* @param interactionManager In interaction manager to use.
|
|
43
|
+
* @param title The title of the interaction.
|
|
44
|
+
* @param content The content of the interaction.
|
|
45
|
+
* @param action The custom action that's treated as main action.
|
|
46
|
+
* @returns true if @constant CommonInteractionResponses.Ok.id was selected, or false if @constant CommonInteractionResponses.Cancel.id was selected.
|
|
47
|
+
*/
|
|
48
|
+
static withCustomAndCancel(interactionManager: InteractionManager, title: string | any, content: string | any, action: InteractionResponse): Promise<boolean>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Interact = void 0;
|
|
4
|
+
const InteractionResponse_1 = require("./InteractionResponse");
|
|
5
|
+
const RelayCommand_1 = require("../commands/RelayCommand");
|
|
6
|
+
const CommonInteractionResponses_1 = require("./CommonInteractionResponses");
|
|
7
|
+
/** A syntactic-sugar util for working with interactions. */
|
|
8
|
+
class Interact {
|
|
9
|
+
/**
|
|
10
|
+
* Interact with the user with default OK and Cancel responses.
|
|
11
|
+
* @param interactionManager In interaction manager to use.
|
|
12
|
+
* @param title The title of the interaction.
|
|
13
|
+
* @param content The content of the interaction.
|
|
14
|
+
* @returns true if @constant CommonInteractionResponses.Ok.id was selected, or false if @constant CommonInteractionResponses.Cancel.id was selected.
|
|
15
|
+
*/
|
|
16
|
+
static async withOkCancel(interactionManager, title, content) {
|
|
17
|
+
return await this.withCustomAndCancel(interactionManager, title, content, CommonInteractionResponses_1.CommonInteractionResponses.ok.action);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Interact with the user with a default OK action.
|
|
21
|
+
* @param interactionManager In interaction manager to use.
|
|
22
|
+
* @param title The title of the interaction.
|
|
23
|
+
* @param content The content of the interaction.
|
|
24
|
+
* @returns Should always return true.
|
|
25
|
+
*/
|
|
26
|
+
static async withOk(interactionManager, title, content) {
|
|
27
|
+
return (await interactionManager.requestInteraction({
|
|
28
|
+
title,
|
|
29
|
+
content,
|
|
30
|
+
responses: [CommonInteractionResponses_1.CommonInteractionResponses.ok.action],
|
|
31
|
+
defaultActionId: CommonInteractionResponses_1.CommonInteractionResponses.ok.id,
|
|
32
|
+
})) === CommonInteractionResponses_1.CommonInteractionResponses.ok.id;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Interact with the user with default OK and Cancel responses, and a command to execute when the OK action is activated.
|
|
36
|
+
* @param interactionManager In interaction manager to use.
|
|
37
|
+
* @param title The title of the interaction.
|
|
38
|
+
* @param content The content of the interaction.
|
|
39
|
+
* @param okCommand The comment to execute when the OK action is selected.
|
|
40
|
+
* @returns true if @constant CommonInteractionResponses.Ok.id was selected, or false if @constant CommonInteractionResponses.Cancel.id was selected.
|
|
41
|
+
*/
|
|
42
|
+
static async withOkCommand(interactionManager, title, content, okCommand) {
|
|
43
|
+
return await this.withCustomAndCancel(interactionManager, title, content, new InteractionResponse_1.InteractionResponse(CommonInteractionResponses_1.CommonInteractionResponses.ok.id, CommonInteractionResponses_1.CommonInteractionResponses.ok.action.title, okCommand));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Interact with the user with default Yes and No responses.
|
|
47
|
+
* @param interactionManager In interaction manager to use.
|
|
48
|
+
* @param title The title of the interaction.
|
|
49
|
+
* @param content The content of the interaction.
|
|
50
|
+
* @returns true if @constant CommonInteractionResponses.Yes.id was selected, or false if @constant CommonInteractionResponses.No.id was selected.
|
|
51
|
+
*/
|
|
52
|
+
static async withYesNo(interactionManager, title, content) {
|
|
53
|
+
return (await interactionManager.requestInteraction({
|
|
54
|
+
title,
|
|
55
|
+
content,
|
|
56
|
+
responses: [CommonInteractionResponses_1.CommonInteractionResponses.yes.action, CommonInteractionResponses_1.CommonInteractionResponses.no.action],
|
|
57
|
+
defaultActionId: CommonInteractionResponses_1.CommonInteractionResponses.yes.id,
|
|
58
|
+
cancelActionId: CommonInteractionResponses_1.CommonInteractionResponses.no.id,
|
|
59
|
+
})) === CommonInteractionResponses_1.CommonInteractionResponses.yes.id;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Interact with the user with a custom action and Cancel action.
|
|
63
|
+
* @param interactionManager In interaction manager to use.
|
|
64
|
+
* @param title The title of the interaction.
|
|
65
|
+
* @param content The content of the interaction.
|
|
66
|
+
* @param action The custom action that's treated as main action.
|
|
67
|
+
* @returns true if @constant CommonInteractionResponses.Ok.id was selected, or false if @constant CommonInteractionResponses.Cancel.id was selected.
|
|
68
|
+
*/
|
|
69
|
+
static async withCustomAndCancel(interactionManager, title, content, action) {
|
|
70
|
+
if (action.command && !('onOperationFinished' in content)) {
|
|
71
|
+
throw Error('Missing onOperationFinished property on the content when using command in responses. Do not forget a default assignment to undefined.');
|
|
72
|
+
}
|
|
73
|
+
return (await interactionManager.requestInteraction({
|
|
74
|
+
title,
|
|
75
|
+
content,
|
|
76
|
+
responses: [
|
|
77
|
+
action,
|
|
78
|
+
new InteractionResponse_1.InteractionResponse(CommonInteractionResponses_1.CommonInteractionResponses.cancel.id, CommonInteractionResponses_1.CommonInteractionResponses.cancel.action.title,
|
|
79
|
+
// A Cancel action that becomes disabled when the OK command (if exists) is active:
|
|
80
|
+
new RelayCommand_1.RelayCommand(() => content?.onOperationFinished?.call(CommonInteractionResponses_1.CommonInteractionResponses.cancel.id), () => action.command?.workingFlag?.isActive !== true)),
|
|
81
|
+
],
|
|
82
|
+
defaultActionId: action.id,
|
|
83
|
+
cancelActionId: CommonInteractionResponses_1.CommonInteractionResponses.cancel.id,
|
|
84
|
+
})) === action.id;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.Interact = Interact;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { InteractionResponse } from './InteractionResponse';
|
|
2
|
+
/** Represents a request object with info on how to interact with the user from the view model. */
|
|
3
|
+
export interface InteractionRequest {
|
|
4
|
+
/** The title of the request. Can be a string, or a view model on VM-first approach. */
|
|
5
|
+
title: string | any;
|
|
6
|
+
/** The content of the request. Can be a string, or a view model on VM-first approach. */
|
|
7
|
+
content: string | any;
|
|
8
|
+
/** A list of possible actions for the user to choose from as a response for the interaction request. */
|
|
9
|
+
responses?: InteractionResponse[];
|
|
10
|
+
/** The ID of the action that is considered to be a cancellation of the flow. */
|
|
11
|
+
cancelActionId?: string;
|
|
12
|
+
/** The ID of the action that is the preferred action for the user. In many cases this action can be highlighted and even be activated with the Enter key. */
|
|
13
|
+
defaultActionId?: string;
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RelayCommand } from "../commands/RelayCommand";
|
|
2
|
+
/** Represents an action for the user to respond on an interaction request. */
|
|
3
|
+
export declare class InteractionResponse {
|
|
4
|
+
/** The ID of the response. */
|
|
5
|
+
readonly id: string;
|
|
6
|
+
/** The title of the response. */
|
|
7
|
+
readonly title: string;
|
|
8
|
+
/** An optional command to invoke as the user responds. */
|
|
9
|
+
readonly command?: RelayCommand<void> | undefined;
|
|
10
|
+
/** An optional icon. */
|
|
11
|
+
readonly icon?: string | undefined;
|
|
12
|
+
constructor(
|
|
13
|
+
/** The ID of the response. */
|
|
14
|
+
id: string,
|
|
15
|
+
/** The title of the response. */
|
|
16
|
+
title: string,
|
|
17
|
+
/** An optional command to invoke as the user responds. */
|
|
18
|
+
command?: RelayCommand<void> | undefined,
|
|
19
|
+
/** An optional icon. */
|
|
20
|
+
icon?: string | undefined);
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InteractionResponse = void 0;
|
|
4
|
+
/** Represents an action for the user to respond on an interaction request. */
|
|
5
|
+
class InteractionResponse {
|
|
6
|
+
constructor(
|
|
7
|
+
/** The ID of the response. */
|
|
8
|
+
id,
|
|
9
|
+
/** The title of the response. */
|
|
10
|
+
title,
|
|
11
|
+
/** An optional command to invoke as the user responds. */
|
|
12
|
+
command,
|
|
13
|
+
/** An optional icon. */
|
|
14
|
+
icon) {
|
|
15
|
+
this.id = id;
|
|
16
|
+
this.title = title;
|
|
17
|
+
this.command = command;
|
|
18
|
+
this.icon = icon;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.InteractionResponse = InteractionResponse;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { InteractionManager } from "./InteractionManager";
|
|
2
|
+
import { InteractionRequest } from "./InteractionRequest";
|
|
3
|
+
export declare class SingleConcurrentInteractionManager implements InteractionManager {
|
|
4
|
+
interactionRequest: InteractionRequest | null;
|
|
5
|
+
private resolveFunc?;
|
|
6
|
+
constructor();
|
|
7
|
+
requestInteraction(interactionRequest: InteractionRequest): Promise<string | void>;
|
|
8
|
+
respond(interactionResponse?: string): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
9
|
+
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
10
|
+
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
11
|
+
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
12
|
+
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
|
|
13
|
+
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
|
|
14
|
+
var _, done = false;
|
|
15
|
+
for (var i = decorators.length - 1; i >= 0; i--) {
|
|
16
|
+
var context = {};
|
|
17
|
+
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
|
|
18
|
+
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
|
|
19
|
+
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
|
|
20
|
+
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
|
|
21
|
+
if (kind === "accessor") {
|
|
22
|
+
if (result === void 0) continue;
|
|
23
|
+
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
|
|
24
|
+
if (_ = accept(result.get)) descriptor.get = _;
|
|
25
|
+
if (_ = accept(result.set)) descriptor.set = _;
|
|
26
|
+
if (_ = accept(result.init)) initializers.unshift(_);
|
|
27
|
+
}
|
|
28
|
+
else if (_ = accept(result)) {
|
|
29
|
+
if (kind === "field") initializers.unshift(_);
|
|
30
|
+
else descriptor[key] = _;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
34
|
+
done = true;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.SingleConcurrentInteractionManager = void 0;
|
|
38
|
+
const mobx_1 = require("mobx");
|
|
39
|
+
let SingleConcurrentInteractionManager = (() => {
|
|
40
|
+
var _a;
|
|
41
|
+
let _instanceExtraInitializers = [];
|
|
42
|
+
let _interactionRequest_decorators;
|
|
43
|
+
let _interactionRequest_initializers = [];
|
|
44
|
+
let _interactionRequest_extraInitializers = [];
|
|
45
|
+
let _requestInteraction_decorators;
|
|
46
|
+
let _respond_decorators;
|
|
47
|
+
return _a = class SingleConcurrentInteractionManager {
|
|
48
|
+
constructor() {
|
|
49
|
+
this.interactionRequest = (__runInitializers(this, _instanceExtraInitializers), __runInitializers(this, _interactionRequest_initializers, { title: '', content: '' }));
|
|
50
|
+
this.resolveFunc = __runInitializers(this, _interactionRequest_extraInitializers);
|
|
51
|
+
(0, mobx_1.makeObservable)(this);
|
|
52
|
+
this.interactionRequest = null;
|
|
53
|
+
}
|
|
54
|
+
requestInteraction(interactionRequest) {
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
this.interactionRequest = interactionRequest;
|
|
57
|
+
this.resolveFunc = resolve;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
respond(interactionResponse) {
|
|
61
|
+
this.interactionRequest = null;
|
|
62
|
+
this.resolveFunc(interactionResponse);
|
|
63
|
+
this.resolveFunc = undefined;
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
(() => {
|
|
67
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
68
|
+
_interactionRequest_decorators = [mobx_1.observable];
|
|
69
|
+
_requestInteraction_decorators = [mobx_1.action];
|
|
70
|
+
_respond_decorators = [mobx_1.action];
|
|
71
|
+
__esDecorate(_a, null, _requestInteraction_decorators, { kind: "method", name: "requestInteraction", static: false, private: false, access: { has: obj => "requestInteraction" in obj, get: obj => obj.requestInteraction }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
72
|
+
__esDecorate(_a, null, _respond_decorators, { kind: "method", name: "respond", static: false, private: false, access: { has: obj => "respond" in obj, get: obj => obj.respond }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
73
|
+
__esDecorate(null, null, _interactionRequest_decorators, { kind: "field", name: "interactionRequest", static: false, private: false, access: { has: obj => "interactionRequest" in obj, get: obj => obj.interactionRequest, set: (obj, value) => { obj.interactionRequest = value; } }, metadata: _metadata }, _interactionRequest_initializers, _interactionRequest_extraInitializers);
|
|
74
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
75
|
+
})(),
|
|
76
|
+
_a;
|
|
77
|
+
})();
|
|
78
|
+
exports.SingleConcurrentInteractionManager = SingleConcurrentInteractionManager;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './InteractionManager';
|
|
2
|
+
export * from './InteractionRequest';
|
|
3
|
+
export * from './wireUpSingleConcurrentInteractionHandler';
|
|
4
|
+
export * from './SingleConcurrentInteractionManager';
|
|
5
|
+
export * from './ReportInteractionOperationFinished';
|
|
6
|
+
export * from './Interact';
|
|
7
|
+
export * from './InteractionResponse';
|
|
8
|
+
export * from './CommonInteractionResponses';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./InteractionManager"), exports);
|
|
18
|
+
__exportStar(require("./InteractionRequest"), exports);
|
|
19
|
+
__exportStar(require("./wireUpSingleConcurrentInteractionHandler"), exports);
|
|
20
|
+
__exportStar(require("./SingleConcurrentInteractionManager"), exports);
|
|
21
|
+
__exportStar(require("./ReportInteractionOperationFinished"), exports);
|
|
22
|
+
__exportStar(require("./Interact"), exports);
|
|
23
|
+
__exportStar(require("./InteractionResponse"), exports);
|
|
24
|
+
__exportStar(require("./CommonInteractionResponses"), exports);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SingleConcurrentInteractionManager } from './SingleConcurrentInteractionManager';
|
|
2
|
+
export declare function wireUpSingleConcurrentInteractionHandler(interactionManager: SingleConcurrentInteractionManager): {
|
|
3
|
+
isCancelable: boolean | undefined;
|
|
4
|
+
nonCommonActionExists: boolean;
|
|
5
|
+
onModalKeyDown: (e: {
|
|
6
|
+
key: string;
|
|
7
|
+
stopPropagation: Function;
|
|
8
|
+
}) => void;
|
|
9
|
+
} | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wireUpSingleConcurrentInteractionHandler = wireUpSingleConcurrentInteractionHandler;
|
|
4
|
+
const CommonInteractionResponses_1 = require("./CommonInteractionResponses");
|
|
5
|
+
const commonActionIds = [
|
|
6
|
+
CommonInteractionResponses_1.CommonInteractionResponses.ok.id,
|
|
7
|
+
CommonInteractionResponses_1.CommonInteractionResponses.cancel.id,
|
|
8
|
+
CommonInteractionResponses_1.CommonInteractionResponses.yes.id,
|
|
9
|
+
CommonInteractionResponses_1.CommonInteractionResponses.no.id,
|
|
10
|
+
CommonInteractionResponses_1.CommonInteractionResponses.close.id,
|
|
11
|
+
];
|
|
12
|
+
function containsNonCommonAction(actions) {
|
|
13
|
+
if (!actions) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return actions.some(action => !commonActionIds.includes(action.id));
|
|
17
|
+
}
|
|
18
|
+
function wireUpSingleConcurrentInteractionHandler(interactionManager) {
|
|
19
|
+
const { interactionRequest } = interactionManager;
|
|
20
|
+
if (!interactionRequest) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (!(typeof interactionRequest.content === 'string') && 'onOperationFinished' in interactionRequest.content) {
|
|
24
|
+
// Register a handler for the onOperationFinished event that will end the interaction:
|
|
25
|
+
interactionRequest.content.onOperationFinished = (actionId) => interactionManager.respond(actionId);
|
|
26
|
+
}
|
|
27
|
+
const onModalKeyDown = (e) => {
|
|
28
|
+
if (!interactionRequest.defaultActionId || !interactionRequest.responses || e.key !== 'Enter') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const defaultAction = interactionRequest.responses.find(a => a.id === interactionRequest.defaultActionId);
|
|
32
|
+
if (defaultAction?.command) {
|
|
33
|
+
if (defaultAction.command.canExecute()) {
|
|
34
|
+
defaultAction.command.execute();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (defaultAction) {
|
|
38
|
+
interactionManager.respond(interactionRequest.defaultActionId);
|
|
39
|
+
}
|
|
40
|
+
e.stopPropagation();
|
|
41
|
+
};
|
|
42
|
+
const nonCommonActionExists = containsNonCommonAction(interactionRequest.responses);
|
|
43
|
+
const isCancelable = interactionRequest.responses?.some(a => a.id === interactionRequest.cancelActionId);
|
|
44
|
+
return { isCancelable, nonCommonActionExists, onModalKeyDown };
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mvvm-mobx",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MVVM pattern implementation with MobX for reactive state management",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"build:watch": "tsc --watch",
|
|
13
|
+
"test": "jest --passWithNoTests",
|
|
14
|
+
"test:watch": "jest --watch",
|
|
15
|
+
"lint": "eslint src --ext .ts",
|
|
16
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
17
|
+
"format": "prettier --write \"src/**/*.ts\"",
|
|
18
|
+
"clean": "rm -rf dist",
|
|
19
|
+
"prepublishOnly": "npm run build && npm run test"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/haim-b/mvvm-mobx.git"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mvvm",
|
|
27
|
+
"mobx",
|
|
28
|
+
"state-management",
|
|
29
|
+
"reactive"
|
|
30
|
+
],
|
|
31
|
+
"author": "haim-b",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/jest": "^29.5.0",
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
37
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
38
|
+
"eslint": "^8.0.0",
|
|
39
|
+
"jest": "^29.5.0",
|
|
40
|
+
"logger-interface": "^0.1.1",
|
|
41
|
+
"mobx": "^6.0.0",
|
|
42
|
+
"prettier": "^3.0.0",
|
|
43
|
+
"ts-jest": "^29.1.0",
|
|
44
|
+
"typescript": "^5.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"logger-interface": "^0.1.1",
|
|
48
|
+
"mobx": "^6.0.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"mobx": {
|
|
52
|
+
"optional": false
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|