Bläddra i källkod

Allow formulas to define validation functions (#272)

trethaller 1 vecka sedan
förälder
incheckning
0676172ef6
5 ändrade filer med 69 tillägg och 8 borttagningar
  1. 3 0
      bin/cdb.css
  2. 6 0
      bin/cdb.less
  3. 1 0
      hide/comp/cdb/Editor.hx
  4. 44 8
      hide/comp/cdb/Formulas.hx
  5. 15 0
      hide/comp/cdb/Line.hx

+ 3 - 0
bin/cdb.css

@@ -223,6 +223,9 @@
 .cdb .cdb-sheet tr {
 .cdb .cdb-sheet tr {
   border-bottom: 1px solid #333;
   border-bottom: 1px solid #333;
 }
 }
+.cdb .cdb-sheet tr.validation-error td {
+  background-color: rgba(255, 0, 0, 0.3);
+}
 .cdb .cdb-sheet tr.list {
 .cdb .cdb-sheet tr.list {
   background-color: black;
   background-color: black;
 }
 }

+ 6 - 0
bin/cdb.less

@@ -236,6 +236,12 @@
 			border-bottom : 1px solid #333;
 			border-bottom : 1px solid #333;
 		}
 		}
 
 
+		tr.validation-error {
+			td {
+				background-color: rgba(255, 0, 0, 0.3);
+			}
+		}
+
 		// ----- types -------
 		// ----- types -------
 
 
 		tr.list {
 		tr.list {

+ 1 - 0
hide/comp/cdb/Editor.hx

@@ -935,6 +935,7 @@ class Editor extends Component {
 		}
 		}
 		line.table.getRealSheet().updateValue(column, line.index, prev);
 		line.table.getRealSheet().updateValue(column, line.index, prev);
 		line.evaluate(); // propagate
 		line.evaluate(); // propagate
+		line.getRootLine().validate();
 		endChanges();
 		endChanges();
 	}
 	}
 
 

+ 44 - 8
hide/comp/cdb/Formulas.hx

@@ -3,6 +3,14 @@ import hscript.Checker;
 
 
 typedef Formula = { name : String, type : String, call : Dynamic -> Null<Float> }
 typedef Formula = { name : String, type : String, call : Dynamic -> Null<Float> }
 
 
+enum ValidationResult {
+	Ok;
+	Error(message: String);
+}
+
+typedef ValidationFunction = { name : String, type : String, call : Dynamic -> ValidationResult }
+
+
 class SheetAccess {
 class SheetAccess {
 	public var form : Formulas;
 	public var form : Formulas;
 	public var name : String;
 	public var name : String;
@@ -60,6 +68,7 @@ class Formulas {
 	var formulas : Array<Formula> = [];
 	var formulas : Array<Formula> = [];
 	var fmap : Map<String, Map<String, Formula>> = [];
 	var fmap : Map<String, Map<String, Formula>> = [];
 	var currentMap : Map<String,Dynamic>;
 	var currentMap : Map<String,Dynamic>;
+	var validationFuncs : Map<String, ValidationFunction> = [];
 
 
 	public var enable = true;
 	public var enable = true;
 
 
@@ -113,6 +122,23 @@ class Formulas {
 		currentMap = null;
 		currentMap = null;
 	}
 	}
 
 
+	public function validateLine(sheet: cdb.Sheet, lineIndex: Int): ValidationResult {
+		var validationFunc = validationFuncs.get(sheet.name);
+		if( validationFunc == null ) return null;
+		
+		var o = sheet.getLines()[lineIndex];
+
+		currentMap = new Map();
+		var omapped = remap(o, sheet);
+		currentMap = null;
+		
+		try {
+			return validationFunc.call(omapped);
+		} catch( e : Dynamic ) {
+			return Error(Std.string(e));
+		}
+	}
+
 	function remap( o : Dynamic, s : cdb.Sheet ) : Dynamic {
 	function remap( o : Dynamic, s : cdb.Sheet ) : Dynamic {
 		var id = s.idCol != null ? Reflect.field(o, s.idCol.name) : null;
 		var id = s.idCol != null ? Reflect.field(o, s.idCol.name) : null;
 		var m = if( id != null ) currentMap.get(s.name+":"+id) else null;
 		var m = if( id != null ) currentMap.get(s.name+":"+id) else null;
@@ -190,7 +216,8 @@ class Formulas {
 
 
 		formulas = [];
 		formulas = [];
 		fmap = new Map();
 		fmap = new Map();
-		var o : Dynamic = { Math : Math };
+		validationFuncs = new Map();
+		var o : Dynamic = { Math : Math, Ok : ValidationResult.Ok, Error : ValidationResult.Error };
 		for( r in refs )
 		for( r in refs )
 			Reflect.setField(o,r.name, r);
 			Reflect.setField(o,r.name, r);
 		var interp = new hscript.JsInterp();
 		var interp = new hscript.JsInterp();
@@ -210,14 +237,18 @@ class Formulas {
 				var value = interp.variables.get(name);
 				var value = interp.variables.get(name);
 				if( value == null ) return;
 				if( value == null ) return;
 				var sname = typeNameToSheet(t);
 				var sname = typeNameToSheet(t);
-				var tmap = fmap.get(sname);
-				if( tmap == null ) {
-					tmap = new Map();
-					fmap.set(sname, tmap);
+				if(StringTools.startsWith(name, "validate")) {
+					validationFuncs.set(sname, { name : name, type : t, call : value });
+				} else {
+					var tmap = fmap.get(sname);
+					if( tmap == null ) {
+						tmap = new Map();
+						fmap.set(sname, tmap);
+					}
+					var f : Formula = { name : name, type : t, call : value };
+					tmap.set(name, f);
+					formulas.push(f);
 				}
 				}
-				var f : Formula = { name : name, type : t, call : value };
-				tmap.set(name, f);
-				formulas.push(f);
 			default:
 			default:
 			}
 			}
 		}
 		}
@@ -359,6 +390,11 @@ class FormulasView extends hide.view.Script {
 		},[]));
 		},[]));
 
 
 		var tstring = check.checker.types.resolve("String");
 		var tstring = check.checker.types.resolve("String");
+
+		var tany = check.checker.types.resolve("Dynamic");
+		check.checker.setGlobal("Ok", tany);
+		check.checker.setGlobal("Error", TFun([{t:tstring,name:"message",opt:false}], tany));
+
 		var _tarray = check.checker.types.resolve("Array");
 		var _tarray = check.checker.types.resolve("Array");
 		if( tstring == null ) {
 		if( tstring == null ) {
 			var cstring = check.checker.types.defineClass("String");
 			var cstring = check.checker.types.defineClass("String");

+ 15 - 0
hide/comp/cdb/Line.hx

@@ -60,6 +60,7 @@ class Line extends Component {
 	public function syncClasses() {
 	public function syncClasses() {
 		var obj = obj;
 		var obj = obj;
 		element.get(0).classList.toggle("locIgnored", Reflect.hasField(obj,cdb.Lang.IGNORE_EXPORT_FIELD));
 		element.get(0).classList.toggle("locIgnored", Reflect.hasField(obj,cdb.Lang.IGNORE_EXPORT_FIELD));
+		validate();
 	}
 	}
 
 
 	public function getGroupID() {
 	public function getGroupID() {
@@ -117,4 +118,18 @@ class Line extends Component {
 		element.addClass("hidden");
 		element.addClass("hidden");
 	}
 	}
 
 
+	public function validate() {
+        var result = table.editor.formulas.validateLine(table.getRealSheet(), index);
+		if(result == null) return;
+
+        element.removeClass("validation-error");
+		element.attr("title", null);
+        
+		switch(result) {
+			case Error(msg):
+				element.addClass("validation-error");
+				element.attr("title", msg);
+			default:
+		}
+    }
 }
 }