1 module uim.vue.base.component; 2 3 import uim.vue; 4 5 @safe: 6 7 @safe: 8 9 class DVUEComponent : DVUEObj { 10 this() { super(); } 11 this(DVUEApp anApp) { this().app(anApp); } 12 this(string aName) { this().name(aName); } 13 this(DVUEApp anApp, string aName) { this(anApp).name(aName); } 14 15 mixin(TProperty!("DVUEApp", "app")); 16 17 string[] _classes; 18 auto classes() { return _classes; } 19 O classes(this O)(string[] entries...) { _classes ~= entries; return cast(O)this; } 20 O classes(this O)(string[] entries) { _classes ~= entries; return cast(O)this; } 21 unittest { 22 } 23 24 /** 25 * filters - allows to define filters that can be used to apply common text formatting 26 * Example (pretty version, default is minimized) 27 * VUEComponent.filters("capitalize", "if (!value) return '';value = value.toString();return value.charAt(0).toUpperCase() + value.slice(1);") 28 * filters: { 29 * capitalize: function (value) { 30 * if (!value) return ''; 31 * value = value.toString(); 32 * return value.charAt(0).toUpperCase() + value.slice(1); 33 * } 34 * } 35 */ 36 mixin(XStringAA!"filters"); 37 unittest { 38 // assert(VUEComponent.filters("capitalize", "if (!value) return '';value = value.toString();return value.charAt(0).toUpperCase() + value.slice(1);").filters("capitalize").length > 0); 39 } 40 41 mixin(XStringAA!"props"); 42 O props(this O)(string name, string datatype, string defaultValue, bool required = false) { 43 string[] results; 44 results ~= (datatype ? "type:%s".format(datatype) : "type:String"); 45 if (defaultValue) results ~= (defaultValue.indexOf("return") >= 0 ? "default:function(){%s}".format(defaultValue) : "default:%s".format(defaultValue)); 46 if (required) results ~= "required:true"; 47 _props[name] = results.join(","); 48 return cast(O)this; } 49 O props(this O)(string name, string datatype, string defaultValue, string validate, bool required = false) { 50 string[] results; 51 results ~= (datatype ? "type:%s".format(datatype) : "type:String"); 52 if (defaultValue) results ~= (defaultValue.indexOf("return") >= 0 ? "default:function(){%s}".format(defaultValue) : "default:%s".format(defaultValue)); 53 if (validate) results ~= "validator:function(value){"~validate~"}"; 54 if (required) results ~= "required:true"; 55 _props[name] = results.join(","); 56 return cast(O)this; } 57 unittest { 58 assert(VUEComponent.props("a", "x").props == ["a":"x"]); 59 assert(VUEComponent.props(["a":"b"]).props == ["a":"b"]); 60 } 61 62 mixin(XString!"render"); 63 unittest { 64 assert(VUEComponent.render("a").render == "a"); 65 assert(VUEComponent.render("a").render("x").render == "ax"); 66 assert(VUEComponent.render("a").clearRender.render == null); 67 } 68 69 /** 70 * extends - Allows declaratively extending another component without having to use Vue.extend. 71 * This is primarily intended to make it easier to extend between single file components. 72 */ 73 mixin(XStringArray!"extends"); 74 unittest { 75 assert(VUEComponent.extends("a").extends == ["a"]); 76 assert(VUEComponent.extends(["a","b"]).extends == ["a","b"]); 77 assert(VUEComponent.extends("a").extends("x").extends == ["a", "x"]); 78 // TODO: assert(VUEComponent.extends("a").extends("x").removeExtends("a").extends == ["x"]); 79 assert(VUEComponent.extends(["a","b"]).clearExtends.extends == null); 80 } 81 82 mixin(XString!"script"); 83 unittest { 84 assert(VUEComponent.script("a").script == "a"); 85 assert(VUEComponent.script("a").script("x").script == "ax"); 86 assert(VUEComponent.script("a").clearScript.script == null); 87 } 88 89 mixin(XString!"style"); 90 unittest { 91 assert(VUEComponent.style("a").style == "a"); 92 assert(VUEComponent.style("a").style("x").style == "ax"); 93 assert(VUEComponent.style("a").clearStyle.style == null); 94 } 95 96 // imports 97 O imports(this O)(DVUEComponent[] someComponents...) { this.components(someComponents); return cast(O)this; } 98 O imports(this O)(DVUEComponent[] someComponents) { this.components(someComponents); return cast(O)this; } 99 100 O imports(this O)(DVUEModule aModule) { this.imports(aModule.name, "../module/"~aModule.name~".js"); return cast(O)this; } 101 O imports(this O)(DVUEMixin aMixin) { this.imports(aMixin.name, "../mixin/"~aMixin.name~".js"); return cast(O)this; } 102 O imports(this O)(string name, string path) { this.imports(name~" from '"~path~"'"); return cast(O)this; } 103 O imports(this O)(string text) { _imports ~= "import "~text~";"; return cast(O)this; } 104 105 /** 106 * components - Local registration of components 107 * For each property in the components object, 108 * the key will be the name of the custom element, while the value will contain the options object for the component. 109 * Key and name could be the same. Example: 110 * components: { 111 * ComponentA: ComponentA, 112 * 'component-b': ComponentB 113 * } 114 */ 115 protected string[string] _components; 116 auto components() { return _components; } 117 O clearComponents(this O)() { _components = null; return cast(O)this; } 118 O components(this O)(string[] someComponents...) { foreach(name; someComponents) this.components(name, name); return cast(O)this; } 119 O components(this O)(string[string] someComponents) { foreach(key, name; someComponents) component(key, name); return cast(O)this; } 120 O components(this O)(string name, string aComponent) { _components[name] = aComponent; return cast(O)this; } 121 unittest { 122 assert(VUEComponent("test").components("componentA") == `Vue.component('test',{components:{componentA:componentA}});`); 123 writeln(VUEComponent("test").components("component-a", "componentA")); 124 assert(VUEComponent("test").components("component-a", "componentA") == `Vue.component('test',{components:{'component-a':componentA}});`); 125 } 126 /* ERROR 127 // Local registration using imports 128 O components(this O)(DVUEComponent[] someComponents...) { foreach(c; someComponents) this.component(c); return cast(O)this; } 129 O components(this O)(DVUEComponent[] someComponents) { foreach(c; someComponents) component(c); return cast(O)this; } 130 O component(this O)(DVUEComponent aComponent) { 131 this.imports(aComponent.name, "./"~aComponent.name~".js").component(aComponent.name); 132 if (app) app.components(aComponent); 133 return cast(O)this; } 134 unittest { 135 assert(VUEComponent.component("componentA").components == ["componentA":"componentA"]); 136 assert(VUEComponent.component("'component-a'", "componentA").components == ["'component-a'":"componentA"]); 137 } 138 */ 139 mixin(TProperty!("string", "content")); 140 /** 141 * Mixins are a flexible way to distribute reusable functionalities for Vue components. 142 * A mixin object can contain any component options. When a component uses a mixin, all options in the mixin will be “mixed” into the component’s own options. 143 **/ 144 mixin(XPropertyAA!("string", "DVUEMixin", "mixins")); 145 unittest { 146 auto mixin1 = VUEMixin; 147 auto mixin2 = VUEMixin; 148 assert(VUEComponent.mixins(["test": mixin1]).mixins.length == 1); 149 /* assert(VUEComponent.mixins(["test": mixin1]).mixinsOne("test") == mixin1); 150 assert(VUEComponent.mixins("test", mixin1).mixinsOne("test") == mixin1); 151 assert(VUEComponent.mixins(["test": mixin1, "test2": mixin2]).mixinsOne("test") == mixin1); 152 assert(VUEComponent.mixins(["test": mixin1, "test2": mixin2]).mixinsAll("test") == [mixin1, mixin2]); 153 assert(VUEComponent.mixins("test", mixin1).mixins("test2", mixin2).mixinsOne("test") == mixin1); 154 */ } 155 156 157 string globalRegistration() {/* 158 // debug writeln("Name = ",_name); 159 // debug writeln("Template = ",_template_); 160 // debug writeln("Props = ", _props); 161 // debug writeln("Data = ", _data); 162 */ 163 string result; 164 if (_name) result ~= "'"~_name~"'"; 165 166 auto mySettings = settings; 167 if (_template_) mySettings["template"] = "`%s`".format(_template_); 168 169 if (mySettings) result ~= (result ? ",":"")~mySettings.toJS(true); 170 return "Vue.component("~result~");"; 171 } 172 unittest { 173 assert(VUEComponent("test") == `Vue.component('test');`); 174 assert(VUEComponent("test").template_("xyz") == "Vue.component('test',{template:`xyz`});", 175 "Wrong? -> "~VUEComponent("test").template_("xyz").toString); 176 } 177 178 void request(HTTPServerRequest req, HTTPServerResponse res) { 179 res.writeBody(toString, "text/javascript"); 180 } 181 182 string toVue() { 183 string result; 184 if (_template_) result ~= "<template>"~_template_~"</template>"; 185 if (_script) result ~= "<script>"~_script~"</script>"; 186 if (_style) result ~= "<style scoped>"~_style~"</style>"; 187 return result; 188 } 189 unittest{ 190 /// TODO 191 } 192 193 override string[string] settings() { 194 string[string] results = super.settings; 195 if (_classes) this.computed("classes", `return`~this.classes.toJS~`;`); 196 197 if (_components) { 198 string[] componentsForVue; 199 foreach(kv; _components.byKeyValue) { 200 if (kv.key.indexOf("-") >= 0) componentsForVue ~= "'%s':%s".format(kv.key, kv.value); 201 else componentsForVue ~= "%s:%s".format(kv.key, kv.value); 202 } 203 results["components"] = "{"~componentsForVue.join(",")~"}"; 204 } 205 if (_data) results["data"] = "function(){return"~_data.toJS(true)~"}"; 206 if (_filters) { 207 string[string] filtersForVue; 208 foreach(kv; _filters.byKeyValue) filtersForVue[kv.key] = "function(value){"~kv.value~"}"; 209 results["filters"] = filtersForVue.toJS; 210 } 211 if (_mixins) results["mixins"] = _mixins.toJS; 212 if (_props) { 213 string[string] propsForVue; 214 foreach(kv; props.byKeyValue) { propsForVue[kv.key] = "{"~kv.value~"}"; } 215 results["props"] = propsForVue.toJS; 216 } 217 if (_watch) results["watch"] = _watch.toJS; 218 219 return results; 220 } 221 unittest{ 222 /// TODO 223 } 224 225 string toVue3(string target) { 226 string result; 227 result ~= target~`.component('%s',%s)`.format(_name, settings.toJS(true)); 228 return result; 229 } 230 unittest { 231 writeln(VUEComponent.toVue3("test")); 232 assert(VUEComponent.toVue3("test") == "test.component('my-component-name', {`~settings.toJS(true)~`})`"); 233 } 234 235 override bool opEquals(string txt) { return toString == txt; } 236 override string toString() { return globalRegistration; } 237 unittest{ 238 /// TODO 239 } 240 } 241 auto VUEComponent() { return new DVUEComponent(); } 242 auto VUEComponent(string aName) { return new DVUEComponent(aName); } 243 auto VUEComponent(DVUEApp anApp) { return new DVUEComponent(anApp); } 244 auto VUEComponent(DVUEApp anApp, string aName) { return new DVUEComponent(anApp, aName); } 245 unittest { 246 assert(VUEComponent("xxx") == "Vue.component('xxx');"); 247 assert(VUEComponent.name("xxx") == "Vue.component('xxx');"); 248 assert(VUEComponent.name("xxx").template_("<h1>hello</h1>") == "Vue.component('xxx',{template:`<h1>hello</h1>`});"); 249 assert(VUEComponent.name("xxx").template_("<h1>hello</h1>").data(["x":"xx", "y":"yy"]) == "Vue.component('xxx',{data:function(){return{x:xx,y:yy}},template:`<h1>hello</h1>`});"); 250 // test computed 251 assert(VUEComponent.computed("a","return b;") == "Vue.component({computed:{a:function(){return b;}}});", 252 "Wrong? -> "~VUEComponent.computed("a","return b;").toString); 253 assert(VUEComponent.computed("a","return b;").computed("x","return z;") == "Vue.component({computed:{a:function(){return b;},x:function(){return z;}}});", 254 "Wrong? -> "~VUEComponent.computed("a","return b;").computed("x","return z;").toString); 255 assert(VUEComponent.computed(["a":"return b;", "x":"return z;"]) == "Vue.component({computed:{a:function(){return b;},x:function(){return z;}}});", 256 "Wrong? -> "~VUEComponent.computed(["a":"return b;", "x":"return z;"]).toString); 257 // test methods 258 assert(VUEComponent.methods("a()","return b;") == "Vue.component({methods:{a(){return b;}}});", 259 "Wrong? -> "~VUEComponent.methods("a()","return b;").toString); 260 assert(VUEComponent.methods("a()","return b;").methods("x()","return z;") == "Vue.component({methods:{a(){return b;},x(){return z;}}});", 261 "Wrong? -> "~VUEComponent.methods("a()","return b;").methods("x()","return z;").toString); 262 assert(VUEComponent.methods(["a()":"return b;", "x()":"return z;"]) == "Vue.component({methods:{a(){return b;},x(){return z;}}});", 263 "Wrong? -> "~VUEComponent.methods(["a()":"return b;", "x()":"return z;"]).toString); 264 }