Skip to content

Commit c2006b7

Browse files
committed
Feature: Emitted Events from Actionable Components; Extended Types and Interfaces of the Component Configuration
1 parent 5edb731 commit c2006b7

File tree

8 files changed

+301
-30
lines changed

8 files changed

+301
-30
lines changed

src/Util/Util.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,10 @@ export type KeyOfType<T, KeyType extends string | number | symbol = KeyTypes<T>>
1212
keyof T,
1313
KeyType
1414
>;
15+
16+
export type TypeOfLastTupleElement<T extends any[]> = T extends [...any[], infer R] ? R : never;
17+
18+
export type ValueOf<T> = T[keyof T];
19+
20+
// https://stackoverflow.com/a/75088992/14804461
21+
export type StringLiteralList<T, K extends keyof T> = T[keyof Pick<T, K>];

src/components/BaseComponent/BaseComponent.ts

Lines changed: 96 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { StoreAPI, JSONPathExpression } from "@/stores/Store";
22
import { ensurePathExists } from "@/stores/Store";
33
import { unref, computed, ref } from "vue";
4-
import type { Ref, ComputedRef, StyleValue } from "vue";
4+
import type { Ref, ComputedRef, StyleValue, EmitFn } from "vue";
5+
import type { TypeOfLastTupleElement } from "@/Util/Util";
56

67
/**
78
* A description of a user-facing method of a component.
@@ -56,11 +57,34 @@ export interface ComponentProps {
5657
style?: StyleValue;
5758
}
5859

60+
/**
61+
* Base-type of a private emitted event of a component. Private events are meant to be consumed only by other components.
62+
*/
63+
export type PrivateComponentEmit = [];
64+
65+
/**
66+
* The payload of an public action-event.
67+
*/
68+
export interface ActionPayload {}
69+
70+
/**
71+
* Base-type of a public emitted event of a component.
72+
* Public events are meant to be consumed by the execution engine that executes the component.
73+
*/
74+
export type PublicComponentEmit = [type: string, action: string, payload: ActionPayload] &
75+
PrivateComponentEmit;
76+
5977
/**
6078
* Eventual emitted events of a component. Parent components can listen to these events.
61-
* Especially useful when nesting components.
79+
*
80+
* These should be consumed by the execution engine for the individual components.
81+
*
82+
* Also useful when nesting components.
6283
*/
63-
export type ComponentEmits = Record<string, any[]>;
84+
export interface ComponentEmits {
85+
[key: string]: PrivateComponentEmit;
86+
}
87+
// Record<string, PrivateComponentEmit>;
6488

6589
/**
6690
* The type of a component.
@@ -86,6 +110,20 @@ export type SerialisedContextMenu = {
86110
*/
87111
export type ValidationConfiguration = {};
88112

113+
/**
114+
* There may be multiple validation strategies for a component.
115+
* It is advised to utilize the ValidationStrategy-interface and its type property to distinguish between different strategies.
116+
*/
117+
export interface ValidationStrategy extends ValidationConfiguration {
118+
type: string;
119+
}
120+
121+
/**
122+
* If multiple strategies are used, the strategies are to be acessed via the InputFieldValidationConfigurationMap-type.
123+
*/
124+
// export type ValidationConfigurationMap = Record<string, ValidationStrategy>;
125+
export interface ValidationConfigurationMap {}
126+
89127
/**
90128
* Serialised dependencies of a component.
91129
*/
@@ -136,6 +174,28 @@ export type ComponentState = {
136174
isCorrect: boolean;
137175
};
138176

177+
/**
178+
* The serialized configuration of a component action.
179+
*/
180+
export interface ComponentAction {
181+
type: string;
182+
action: string;
183+
}
184+
185+
/**
186+
* The fetch action is a special action that is used to fetch data from an external source.
187+
*/
188+
export interface FetchAction extends ComponentAction {
189+
type: "fetch";
190+
}
191+
192+
/**
193+
* The serialized configuration for the actions of a component.
194+
*/
195+
export interface ComponentActions {
196+
[key: string]: ComponentAction;
197+
}
198+
139199
/**
140200
* Generic type description with defaults of a serialised base component.
141201
*/
@@ -176,6 +236,10 @@ export interface SerializedBaseComponent<T extends BaseComponentType = BaseCompo
176236
* Optional: Nested components of the component.
177237
*/
178238
nestedComponents?: NestedComponents;
239+
/**
240+
* Optional: Actions that can be emitted by the component.
241+
*/
242+
actions?: ComponentActions;
179243
}
180244

181245
/**
@@ -185,6 +249,7 @@ export interface ComponentTypeSpecification {
185249
SerializedComponent: SerializedBaseComponent;
186250
Dependencies: ComponentDependencies;
187251
MethodImplementations: ExposedMethods;
252+
Emits: ComponentEmits;
188253
}
189254

190255
/**
@@ -354,4 +419,32 @@ export abstract class BaseComponent<
354419
<T["MethodImplementations"]>{}
355420
);
356421
};
422+
423+
/**
424+
* Returns the action configuration for a specific action of the component.
425+
* @param event String that associates with an action of the component.
426+
* @returns
427+
*/
428+
public getActionConfig = (event: string) => {
429+
return this.serializedBaseComponent.value.actions?.[event] as ComponentAction;
430+
};
431+
432+
/**
433+
* The actionHandler function is used to emit an action event.
434+
* @param emit
435+
* @param event
436+
*/
437+
public actionHandler = (emit: EmitFn, event: string) => {
438+
const payload = this.constructPayload?.();
439+
const { type, action } = this.getActionConfig(event);
440+
441+
emit("action", type, action, payload);
442+
};
443+
444+
/**
445+
* The constructPayload function is used to construct the payload of an action event.
446+
* The function must be implemented in the derived component class.
447+
* @returns
448+
*/
449+
protected constructPayload?(): ActionPayload | TypeOfLastTupleElement<T["Emits"][string]>;
357450
}

src/components/CodeEditor/CodeEditor.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import type {
55
ComponentProps,
66
ComponentState,
77
ComponentTypeSpecification,
8-
ComponentConfiguration
8+
ComponentConfiguration,
9+
ValidationStrategy,
10+
ValidationConfigurationMap,
11+
ValidationConfiguration
912
} from "@/components/BaseComponent/BaseComponent";
1013
import { BaseComponent } from "@/components/BaseComponent/BaseComponent";
1114
import type { JSONPathExpression } from "@/stores/Store";
@@ -25,33 +28,89 @@ export type CodeEditorComponentType = "CodeEditor";
2528
* The CodeEditor-component requires the following dependencies. The paths to the dependencies are defined here via JSONPathExpression.
2629
*/
2730
export interface SerializedCodeEditorDependencies extends SerialisedDependencies {
31+
/**
32+
* The globalDarkMode-property is a JSONPathExpression that may point to the user's dark mode preference.
33+
*/
2834
globalDarkMode?: JSONPathExpression;
35+
/**
36+
* The code-property is a JSONPathExpression that may point to the user's code.
37+
*/
2938
code?: JSONPathExpression;
39+
/**
40+
* The referenceCode-property is a JSONPathExpression that points to the reference code that the user's code will be compared against.
41+
*/
42+
referenceCode?: JSONPathExpression;
3043
}
3144

3245
/**
3346
* The CodeEditor-component requires the following dependencies. The types of the dependencies are defined here.
3447
*/
3548
export interface CodeEditorDependencies extends ComponentDependencies {
49+
/**
50+
* The globalDarkMode-property is a boolean that may be used to determine the dark mode of the CodeEditor.
51+
*/
3652
globalDarkMode?: boolean | undefined;
53+
/**
54+
* The code-property is a string that may contains the user's code.
55+
*/
3756
code?: string;
57+
/**
58+
* The referenceCode-property is a string that mcontains the reference code that the user's code may be compared against.
59+
*/
60+
referenceCode?: string;
3861
}
3962

4063
/**
4164
* The CodeEditor-component may require state handling. This state is defined here.
4265
*/
4366
export interface CodeEditorComponentState extends ComponentState {
67+
/**
68+
* The code-property is a string that contains the user's code.
69+
*/
4470
code: string;
4571
}
4672

4773
/**
4874
* The CodeEditor-component may have configuration options. These options are defined here.
4975
*/
5076
export interface CodeEditorConfiguration extends ComponentConfiguration {
77+
/**
78+
* The darkMode-property is a boolean that may be used to determine the dark mode of the CodeEditor.
79+
*/
5180
darkMode?: boolean;
81+
/**
82+
* The language-property is a string that may be used to determine the syntax highlighting of the CodeEditor.
83+
*/
5284
language?: SupportedLanguages;
5385
}
5486

87+
/**
88+
* The CodeEditor allows for optional validation strategies.
89+
* The first strategy is a basic string compare.
90+
* The second strategy is a request-based strategy, which may depend on a user-defined strategy (e.g. test-suite, output comparisons, more complex string comparisons, etc.).
91+
* As the second strategy may depend on more than just the code, the entire state is accessible.
92+
*/
93+
export type CodeEditorValidationConfiguration<
94+
K extends keyof CodeEditorValidationConfigurationMap
95+
> = { [P in K]: { type: P } & CodeEditorValidationConfigurationMap[P] }[K] &
96+
ValidationConfiguration;
97+
98+
export interface StringComparison extends ValidationStrategy {
99+
type: "compareToString";
100+
}
101+
102+
export interface ExternalValidation extends ValidationStrategy {
103+
type: "validateExternally";
104+
}
105+
106+
/**
107+
* Map of all available validation strategies for the CodeEditor component.
108+
*/
109+
interface CodeEditorValidationConfigurationMap extends ValidationConfigurationMap {
110+
validateExternally: ExternalValidation;
111+
compareToString: StringComparison;
112+
}
113+
55114
/**
56115
* The SerializedCodeEditorComponent interface is used to define the serialised properties of the CodeEditor component.
57116
*/
@@ -60,6 +119,9 @@ export interface SerializedCodeEditorComponent
60119
dependencies: SerializedCodeEditorDependencies;
61120
state: CodeEditorComponentState;
62121
componentConfiguration?: CodeEditorConfiguration;
122+
validationConfiguration?: CodeEditorValidationConfiguration<
123+
keyof CodeEditorValidationConfigurationMap
124+
>;
63125
}
64126

65127
export interface CodeEditorSpecification extends ComponentTypeSpecification {
@@ -71,13 +133,29 @@ export interface CodeEditorSpecification extends ComponentTypeSpecification {
71133
* The CodeEditorComponent class is a derived taskComponent, that displays a Graph specified in the Graphviz-DOT language.
72134
*/
73135
export class CodeEditorComponent extends BaseComponent<CodeEditorSpecification> {
136+
private validationStrategies: {
137+
[K in keyof CodeEditorValidationConfigurationMap]: (
138+
taskState: string,
139+
functionConfig: CodeEditorValidationConfiguration<K>
140+
) => boolean;
141+
} = {
142+
validateExternally: this.externalValidation.bind(this),
143+
compareToString: this.stringComparison.bind(this)
144+
};
74145
/**
75146
* This function determines when a CodeEditorComponent is valid and when it is correct.
76147
* @returns
77148
*/
78-
public validate() {
149+
public validate(userCode: string) {
79150
const validity = { isValid: false, isCorrect: false };
80151

81152
return validity;
82153
}
154+
155+
private externalValidation(userCode: string, validationConfiguration: ExternalValidation) {
156+
return true;
157+
}
158+
private stringComparison(userCode: string, validationConfiguration: StringComparison) {
159+
return true;
160+
}
83161
}

src/components/GenericButton/GenericButton.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import type {
55
ComponentProps,
66
ComponentState,
77
ComponentTypeSpecification,
8-
ComponentConfiguration
8+
ComponentConfiguration,
9+
ComponentEmits
910
} from "@/components/BaseComponent/BaseComponent";
1011
import { BaseComponent } from "@/components/BaseComponent/BaseComponent";
1112
import type { IconList } from "@/Util/IconList";
@@ -17,10 +18,17 @@ export interface ButtonProps extends ComponentProps {
1718
isValid: boolean;
1819
}
1920

21+
/**
22+
* The ClickEmit type is used to define the emitted event of the Button component.
23+
*/
24+
export type ClickEmit = [];
25+
2026
/**
2127
* The Button component emitts an event on click.
2228
*/
23-
export type ButtonEmits = { buttonClick: [] };
29+
export type ButtonEmits = {
30+
buttonClick: ClickEmit;
31+
} & ComponentEmits;
2432

2533
/**
2634
* The type of the Button component.
@@ -89,6 +97,7 @@ export interface SerializedButtonComponent extends SerializedBaseComponent<Butto
8997
export interface ButtonSpecification extends ComponentTypeSpecification {
9098
SerializedComponent: SerializedButtonComponent;
9199
Dependencies: ButtonDependencies;
100+
Emits: ButtonEmits;
92101
}
93102

94103
/**

0 commit comments

Comments
 (0)