Skip to content

Commit 938bcbd

Browse files
committed
feat: init conditions and fields handlers
1 parent 32b3468 commit 938bcbd

12 files changed

Lines changed: 308 additions & 0 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class AndCondition implements ConditionInterface {
2+
constructor(private conditions: Condition[]) {
3+
}
4+
5+
public validate(): boolean {
6+
return true;
7+
}
8+
9+
public getConditionFieldNames(): string[] {
10+
return this.conditions.flatMap(condition => condition.field ? [condition.field] : []);
11+
}
12+
}
13+
14+
export default AndCondition;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class NullCondition implements ConditionInterface {
2+
public validate(): boolean {
3+
return true;
4+
}
5+
6+
public getConditionFieldNames(): string[] {
7+
return [];
8+
}
9+
}
10+
11+
export default NullCondition;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class OrCondition implements ConditionInterface {
2+
constructor(private condition: Condition) {
3+
}
4+
5+
public validate(): boolean {
6+
return true;
7+
}
8+
9+
public getConditionFieldNames(): string[] {
10+
return [this.condition.field];
11+
}
12+
}
13+
14+
export default OrCondition;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import AndCondition from "./condition/andCondition";
2+
import NullCondition from "./condition/nullCondition";
3+
import OrCondition from "./condition/orCondition";
4+
5+
class ConditionBuilder implements ConditionBuilderInterface {
6+
public build(conditions: any): ConditionInterface[] {
7+
if (!Array.isArray(conditions) || conditions.length === 0) {
8+
return [new NullCondition()];
9+
}
10+
11+
let conditionsList: ConditionInterface[] = [];
12+
conditions.forEach(conditionSet => {
13+
if (!Array.isArray(conditionSet) || conditionSet.length === 0) {
14+
conditionsList.push(new NullCondition());
15+
}
16+
17+
if (conditionSet.length === 1) {
18+
conditionsList.push(new OrCondition(conditionSet[0]));
19+
} else {
20+
conditionsList.push(new AndCondition(conditionSet));
21+
}
22+
});
23+
24+
if (conditionsList.length > 0) {
25+
return conditionsList;
26+
}
27+
28+
return [new NullCondition()];
29+
}
30+
}
31+
32+
export default ConditionBuilder;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
interface ConditionBuilderInterface {
2+
build(condition: any): ConditionInterface[];
3+
}
4+
5+
interface ConditionInterface {
6+
validate(): boolean;
7+
getConditionFieldNames(): string[];
8+
}
9+
10+
type Condition = {
11+
field: string;
12+
operator: string;
13+
value: any;
14+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
export function evaluateCondition(condition: Condition, currentValue: any): boolean {
2+
switch (condition.operator) {
3+
case '==':
4+
return currentValue == condition.value;
5+
case '===':
6+
return currentValue === condition.value;
7+
case '!=':
8+
return currentValue != condition.value;
9+
case '!==':
10+
return currentValue !== condition.value;
11+
case '>':
12+
return currentValue > condition.value;
13+
case '<':
14+
return currentValue < condition.value;
15+
case '>=':
16+
return currentValue >= condition.value;
17+
case '<=':
18+
return currentValue <= condition.value;
19+
default:
20+
return false;
21+
}
22+
}

source/js/fields/field/checkbox.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
class Checkbox implements FieldInterface {
2+
private valueChangeListeners: FieldInterface[] = [];
3+
4+
constructor(
5+
private field: HTMLElement,
6+
private choices: NodeListOf<HTMLInputElement>,
7+
private name: string,
8+
private conditions: ConditionInterface[]
9+
) {
10+
11+
}
12+
13+
private changeListener() {
14+
15+
}
16+
17+
public getName(): string {
18+
return this.name;
19+
}
20+
21+
public validateConditionals(): boolean {
22+
return true;
23+
}
24+
25+
public addValueChangeListener(field: FieldInterface): void {
26+
this.valueChangeListeners.push(field);
27+
}
28+
29+
public getConditions(): ConditionInterface[] {
30+
return this.conditions;
31+
}
32+
}
33+
34+
export default Checkbox;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class NullField implements FieldInterface {
2+
constructor(
3+
private field: HTMLElement,
4+
type: string,
5+
private name: string,
6+
private conditions: ConditionInterface[]
7+
) {
8+
console.error(`Field type "${type}" is not implemented.`);
9+
}
10+
11+
public getName(): string {
12+
return '';
13+
}
14+
15+
public validateConditionals(): boolean {
16+
return true;
17+
}
18+
19+
public addValueChangeListener(field: FieldInterface): void {
20+
return;
21+
}
22+
23+
public getConditions(): ConditionInterface[] {
24+
return this.conditions;
25+
}
26+
}
27+
28+
export default NullField;

source/js/fields/fieldBuilder.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import Checkbox from "./field/checkbox";
2+
import NullField from "./field/nullField";
3+
4+
class FieldBuilder implements FieldBuilderInterface {
5+
private name: string = 'data-js-field-name';
6+
private condition: string = 'data-js-conditional-logic';
7+
8+
constructor(private conditionBuilder: ConditionBuilderInterface) {
9+
}
10+
11+
public build(field: HTMLElement, type: string): FieldInterface {
12+
if (!this.validateRequiredAttributes(field)) {
13+
console.error('Field name and conditional is required');
14+
return this.buildNullField(field, type);
15+
}
16+
17+
switch (type) {
18+
case 'checkbox':
19+
return this.buildCheckbox(field);
20+
}
21+
22+
return this.buildNullField(field, type);
23+
}
24+
25+
public buildNullField(field: HTMLElement, type: string): FieldInterface {
26+
return new NullField(
27+
field,
28+
type,
29+
this.getFieldName(field),
30+
this.getFieldCondition(field)
31+
);
32+
}
33+
34+
public buildCheckbox(field: HTMLElement): FieldInterface {
35+
const choices = field.querySelectorAll('input[type="checkbox"]');
36+
37+
38+
return new Checkbox(
39+
field,
40+
choices as NodeListOf<HTMLInputElement>,
41+
this.getFieldName(field),
42+
this.getFieldCondition(field)
43+
);
44+
}
45+
46+
private getFieldName(field: HTMLElement): string {
47+
return field.getAttribute('data-js-field-name') as string;
48+
}
49+
50+
private getFieldCondition(field: HTMLElement): ConditionInterface[] {
51+
let condition = 0;
52+
53+
try {
54+
condition = JSON.parse(field.getAttribute('data-js-conditional-logic') as string);
55+
} catch (error) {
56+
condition = 0;
57+
}
58+
59+
return this.conditionBuilder.build(condition);
60+
}
61+
62+
private validateRequiredAttributes(field: HTMLElement): boolean {
63+
[this.name, this.condition].forEach((attribute) => {
64+
if (!field.getAttribute(attribute)) {
65+
console.error(`Field is missing required attribute: ${attribute}`);
66+
return false;
67+
}
68+
});
69+
70+
return true;
71+
}
72+
}
73+
74+
export default FieldBuilder;

source/js/fields/fields.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class Fields implements FieldsInterface {
2+
private fields: FieldsObject = {};
3+
4+
constructor(private form: HTMLFormElement, private fieldsArray: FieldInterface[]) {
5+
}
6+
7+
public init(): void {
8+
this.fieldsArray.forEach((field) => {
9+
this.fields[field.getName()] = field;
10+
});
11+
12+
this.setupConditionals();
13+
}
14+
15+
private setupConditionals(): void {
16+
for (const fieldName in this.fields) {
17+
this.fields[fieldName].getConditions().forEach((condition) => {
18+
condition.getConditionFieldNames().forEach((conditionFieldName) => {
19+
if (this.fields[conditionFieldName]) {
20+
this.fields[conditionFieldName].addValueChangeListener(this.fields[fieldName]);
21+
}
22+
});
23+
});
24+
}
25+
}
26+
}
27+
28+
export default Fields;

0 commit comments

Comments
 (0)