Explorar el Código

- load helios by default
- more css and images
- improvements in control views and controllers

Nicolas Petton hace 10 años
padre
commit
811b7e13eb
Se han modificado 10 ficheros con 517 adiciones y 178 borrados
  1. 62 24
      css/moka.css
  2. 48 9
      css/moka.less
  3. BIN
      images/moka/switch.png
  4. 1 1
      index.html
  5. 62 20
      js/Moka-Controllers.js
  6. 21 7
      js/Moka-Examples.js
  7. 202 70
      js/Moka-Views.js
  8. 30 12
      st/Moka-Controllers.st
  9. 4 0
      st/Moka-Examples.st
  10. 87 35
      st/Moka-Views.st

+ 62 - 24
css/moka.css

@@ -9,7 +9,7 @@
   user-select: none;
 }
 body {
-  background: #d5d5d5;
+  background: #f1f1f1;
 }
 .moka_view {
   display: inline-block;
@@ -53,9 +53,6 @@ body {
 .moka_view .mk_control:active:focus,
 .moka_view .mk_control:focus:focus {
   outline: 0;
-  -webkit-box-shadow: 0 0 2px #0088cc;
-  -moz-box-shadow: 0 0 2px #0088cc;
-  box-shadow: 0 0 2px #0088cc;
 }
 .moka_view button {
   font-size: 12px;
@@ -75,6 +72,12 @@ body {
   -moz-box-shadow: 0 0 3px 0 #cccccc;
   box-shadow: 0 0 3px 0 #cccccc;
   background: white;
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
   background: #fafafa;
   background: -webkit-gradient(linear, left bottom, left top, color-stop(0, #dedede), color-stop(1, #ffffff));
   background: -ms-linear-gradient(bottom, #dedede, #ffffff);
@@ -84,17 +87,19 @@ body {
 }
 .moka_view button:focus {
   outline: 0;
-  -webkit-box-shadow: 0 0 2px #0088cc;
-  -moz-box-shadow: 0 0 2px #0088cc;
-  box-shadow: 0 0 2px #0088cc;
 }
-.moka_view button:active {
-  background: #dedede;
-  -webkit-box-shadow: inset 0 0 3px 0 #aaaaaa;
-  -moz-box-shadow: inset 0 0 3px 0 #aaaaaa;
-  box-shadow: inset 0 0 3px 0 #aaaaaa;
+.moka_view button:focus {
+  border-color: #08c;
 }
-.moka_view input {
+.moka_view button:active {
+  background: #ccc;
+  -webkit-box-shadow: inset 0 0 3px 0 #888888;
+  -moz-box-shadow: inset 0 0 3px 0 #888888;
+  box-shadow: inset 0 0 3px 0 #888888;
+  border-color: #999;
+}
+.moka_view input,
+.moka_view textarea {
   font-size: 12px;
   font-family: "Open Sans";
   color: #555;
@@ -114,22 +119,26 @@ body {
   background: white;
   text-shadow: 0 0 0;
 }
-.moka_view input:focus {
+.moka_view input:focus,
+.moka_view textarea:focus {
   outline: 0;
-  -webkit-box-shadow: 0 0 2px #0088cc;
-  -moz-box-shadow: 0 0 2px #0088cc;
-  box-shadow: 0 0 2px #0088cc;
 }
-.moka_view input:focus {
-  -webkit-box-shadow: inset 0 0 3px 0 #0088cc;
-  -moz-box-shadow: inset 0 0 3px 0 #0088cc;
-  box-shadow: inset 0 0 3px 0 #0088cc;
+.moka_view input:focus,
+.moka_view textarea:focus {
+  -webkit-box-shadow: inset 0 0 3px 0 #999999;
+  -moz-box-shadow: inset 0 0 3px 0 #999999;
+  box-shadow: inset 0 0 3px 0 #999999;
+  border-color: #08c;
+}
+.moka_view textarea {
+  width: 200px;
+  height: 100px;
 }
 .moka_view input[type="checkbox"] {
   position: absolute;
   left: -999999px;
 }
-.moka_view input[type="checkbox"] + label {
+.moka_view .mk_checkbox + label {
   -webkit-touch-callout: none;
   -webkit-user-select: none;
   -khtml-user-select: none;
@@ -137,16 +146,45 @@ body {
   -ms-user-select: none;
   user-select: none;
   padding: 0 6px;
+  margin: 4px;
   background: url('../images/moka/check.png') 50% 50% no-repeat;
   height: 16px;
   width: 16px;
 }
-.moka_view input[type="checkbox"]:focus + label {
+.moka_view .mk_checkbox:focus + label {
   background-image: url('../images/moka/check-focus.png');
   -webkit-box-shadow: 0 0 2px #0088cc;
   -moz-box-shadow: 0 0 2px #0088cc;
   box-shadow: 0 0 2px #0088cc;
 }
-.moka_view input[type="checkbox"]:checked + label {
+.moka_view .mk_checkbox:checked + label {
   background-image: url('../images/moka/check-active.png');
 }
+.moka_view .mk_switch + label {
+  -webkit-touch-callout: none;
+  -webkit-user-select: none;
+  -khtml-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+  -webkit-border-radius: 20px;
+  -moz-border-radius: 20px;
+  border-radius: 20px;
+  margin: 4px;
+  vertical-align: middle;
+  display: inline-block;
+  height: 20px;
+  width: 59px;
+  border: 1px solid #999;
+  background: url('../images/moka/switch.png') 100% 50% no-repeat;
+  -webkit-transition: background 0.3s ease-out;
+  -moz-transition: background 0.3s ease-out;
+  -o-transition: background 0.3s ease-out;
+  transition: background 0.3s ease-out;
+}
+.moka_view .mk_switch:focus + label {
+  border-color: #08c;
+}
+.moka_view .mk_switch:checked + label {
+  background-position: 0% 50%;
+}

+ 48 - 9
css/moka.less

@@ -46,6 +46,12 @@
 		-moz-box-shadow: @arguments;
 		box-shadow: @arguments;
 	    }
+	    .transition(@property: all, @duration:0.2s, @ease:ease-out) {
+		-webkit-transition: @property @duration @ease;
+		-moz-transition: @property @duration @ease;
+		-o-transition: @property @duration @ease;
+		transition: @property @duration @ease;
+	    }
 	    .no-select {
 		-webkit-touch-callout: none;
 		-webkit-user-select: none;
@@ -59,7 +65,7 @@
 
 	    // TEMP
 	    body {
-		background: #d5d5d5;
+		background: #f1f1f1;
 	    }
 
 	    .moka_view {    
@@ -97,48 +103,81 @@
 
 		    &:focus {
 			outline: 0;
-			.box-shadow(0 0 2px #08c);
 		    }
 		}
 
 		button {
 		    .mk_control;
+		    .no-select;
 		    .gradient(#fafafa, #dedede, white);
 
+		    &:focus {
+			border-color: #08c;
+		    }
+
 		    &:active {
-			background: #dedede;
-			.box-shadow(inset 0 0 3px 0 #aaa);
+			background: #ccc;
+			.box-shadow(inset 0 0 3px 0 #888);
+			border-color: #999;
 		    }
 		}
 		
-		input {
+		input, textarea {
 		    .mk_control;
 		    text-shadow: 0 0 0;
 
 		    &:focus {
-			.box-shadow(inset 0 0 3px 0 #08c);
+			.box-shadow(inset 0 0 3px 0 #999);
+			border-color: #08c;
 		    }
 		}
 		
+		textarea {
+		    width: 200px;
+		    height: 100px;
+		}
+
 		input[type="checkbox"] {
 		    position: absolute;
 		    left: -999999px;
 		}
 
-		input[type="checkbox"] + label {
+		.mk_checkbox + label {
 		    .no-select;
 		    padding: 0 6px;
+		    margin: 4px;
 		    background: url('../images/moka/check.png') 50% 50% no-repeat;
 		    height: 16px;
 		    width: 16px;
 		}
 
-		input[type="checkbox"]:focus + label {
+		.mk_checkbox:focus + label {
 		    background-image: url('../images/moka/check-focus.png');
 		    .box-shadow(0 0 2px #08c);
 		}
 
-		input[type="checkbox"]:checked + label {
+		.mk_checkbox:checked + label {
 		    background-image: url('../images/moka/check-active.png');
 		}
+
+		.mk_switch + label {
+		    .no-select;
+		    .rounded(20px);
+		    margin: 4px;
+		    vertical-align: middle;
+		    display: inline-block;
+		    height: 20px;
+		    width: 59px;
+		    border: 1px solid #999;
+		    background: url('../images/moka/switch.png') 100% 50% no-repeat;
+		    .transition(background, .3s);
+		}
+
+		.mk_switch:focus + label {
+		    border-color: #08c;
+		}
+
+		.mk_switch:checked + label {
+		    background-position: 0% 50%;
+		}
 	    }

BIN
images/moka/switch.png


+ 1 - 1
index.html

@@ -18,7 +18,7 @@
             smalltalk.defaultAmdNamespace = "amber_core";
             smalltalk.initialize();
 
-            smalltalk.Browser._open()
+	    require('amber/helpers').popupHelios();
         }
     );
 </script>

+ 62 - 20
js/Moka-Controllers.js

@@ -2,6 +2,68 @@ define("amber_core/Moka-Controllers", ["amber_vm/smalltalk", "amber_vm/nil", "am
 smalltalk.addPackage('Moka-Controllers');
 smalltalk.packages["Moka-Controllers"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
+smalltalk.addClass('MKAnyKeyInputController', smalltalk.MKAspectController, [], 'Moka-Controllers');
+smalltalk.addMethod(
+smalltalk.method({
+selector: "inputText",
+category: 'accessing]',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(self._view())._value();
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"inputText",{},smalltalk.MKAnyKeyInputController)})},
+args: [],
+source: "inputText\x0a\x09^ self view value",
+messageSends: ["value", "view"],
+referencedClasses: []
+}),
+smalltalk.MKAnyKeyInputController);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "onKeyPressed:",
+category: 'actions',
+fn: function (anEvent){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+self._performActionWith_(self._inputText());
+return self}, function($ctx1) {$ctx1.fill(self,"onKeyPressed:",{anEvent:anEvent},smalltalk.MKAnyKeyInputController)})},
+args: ["anEvent"],
+source: "onKeyPressed: anEvent\x0a\x09self performActionWith: self inputText",
+messageSends: ["performActionWith:", "inputText"],
+referencedClasses: []
+}),
+smalltalk.MKAnyKeyInputController);
+
+
+
+smalltalk.addClass('MKEnterInputController', smalltalk.MKAnyKeyInputController, [], 'Moka-Controllers');
+smalltalk.MKEnterInputController.comment="I am the default controller for `MKInputView`. \x0aActions are performed on 'enter' key press.";
+smalltalk.addMethod(
+smalltalk.method({
+selector: "onKeyPressed:",
+category: 'actions',
+fn: function (anEvent){
+var self=this;
+function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=_st(_st(anEvent)._keyCode()).__eq(_st(_st($String())._cr())._asciiValue());
+if(smalltalk.assert($1)){
+smalltalk.MKEnterInputController.superclass.fn.prototype._onKeyPressed_.apply(_st(self), [anEvent]);
+};
+return self}, function($ctx1) {$ctx1.fill(self,"onKeyPressed:",{anEvent:anEvent},smalltalk.MKEnterInputController)})},
+args: ["anEvent"],
+source: "onKeyPressed: anEvent\x0a\x09anEvent keyCode = String cr asciiValue ifTrue: [\x0a\x09\x09super onKeyPressed: anEvent ]",
+messageSends: ["ifTrue:", "=", "keyCode", "asciiValue", "cr", "onKeyPressed:"],
+referencedClasses: ["String"]
+}),
+smalltalk.MKEnterInputController);
+
+
+
 smalltalk.addClass('MKButtonController', smalltalk.MKAspectController, [], 'Moka-Controllers');
 smalltalk.MKButtonController.comment="I am the default controller for `MKButtonView`.";
 smalltalk.addMethod(
@@ -41,24 +103,4 @@ referencedClasses: []
 smalltalk.MKCheckboxController);
 
 
-
-smalltalk.addClass('MKInputController', smalltalk.MKAspectController, [], 'Moka-Controllers');
-smalltalk.MKInputController.comment="I am the default controller for `MKInputView`.";
-smalltalk.addMethod(
-smalltalk.method({
-selector: "onEnterPressed:",
-category: 'actions',
-fn: function (aString){
-var self=this;
-return smalltalk.withContext(function($ctx1) { 
-self._performActionWith_(aString);
-return self}, function($ctx1) {$ctx1.fill(self,"onEnterPressed:",{aString:aString},smalltalk.MKInputController)})},
-args: ["aString"],
-source: "onEnterPressed: aString\x0a\x09self performActionWith: aString",
-messageSends: ["performActionWith:"],
-referencedClasses: []
-}),
-smalltalk.MKInputController);
-
-
 });

+ 21 - 7
js/Moka-Examples.js

@@ -12,9 +12,11 @@ var self=this;
 function $MKLabelView(){return smalltalk.MKLabelView||(typeof MKLabelView=="undefined"?nil:MKLabelView)}
 function $MKButtonView(){return smalltalk.MKButtonView||(typeof MKButtonView=="undefined"?nil:MKButtonView)}
 function $MKInputView(){return smalltalk.MKInputView||(typeof MKInputView=="undefined"?nil:MKInputView)}
+function $MKTextAreaView(){return smalltalk.MKTextAreaView||(typeof MKTextAreaView=="undefined"?nil:MKTextAreaView)}
 function $MKCheckboxView(){return smalltalk.MKCheckboxView||(typeof MKCheckboxView=="undefined"?nil:MKCheckboxView)}
+function $MKSwitchView(){return smalltalk.MKSwitchView||(typeof MKSwitchView=="undefined"?nil:MKSwitchView)}
 return smalltalk.withContext(function($ctx1) { 
-var $2,$1,$4,$3,$5,$7,$6,$9,$8,$10,$11;
+var $2,$1,$4,$3,$5,$7,$6,$9,$8,$11,$10,$13,$12,$14,$15;
 $2=self._counter();
 $ctx1.sendIdx["counter"]=1;
 $1=_st($MKLabelView())._model_aspect_($2,"count");
@@ -37,18 +39,30 @@ _st($6)._render();
 $ctx1.sendIdx["render"]=3;
 $9=self._counter();
 $ctx1.sendIdx["counter"]=4;
-$8=_st($MKCheckboxView())._model_aspect_($9,"checked");
+$8=_st($MKTextAreaView())._model_aspect_($9,"text");
 $ctx1.sendIdx["model:aspect:"]=4;
 _st($8)._render();
 $ctx1.sendIdx["render"]=4;
-$10=_st($MKButtonView())._model_aspect_(self._counter(),"decrease");
-_st($10)._label_("Decrease");
-$11=_st($10)._render();
+$11=self._counter();
+$ctx1.sendIdx["counter"]=5;
+$10=_st($MKCheckboxView())._model_aspect_($11,"checked");
+$ctx1.sendIdx["model:aspect:"]=5;
+_st($10)._render();
+$ctx1.sendIdx["render"]=5;
+$13=self._counter();
+$ctx1.sendIdx["counter"]=6;
+$12=_st($MKSwitchView())._model_aspect_($13,"checked");
+$ctx1.sendIdx["model:aspect:"]=6;
+_st($12)._render();
+$ctx1.sendIdx["render"]=6;
+$14=_st($MKButtonView())._model_aspect_(self._counter(),"decrease");
+_st($14)._label_("Decrease");
+$15=_st($14)._render();
 return self}, function($ctx1) {$ctx1.fill(self,"build",{},smalltalk.MKCounterBuilder)})},
 args: [],
-source: "build\x0a\x09(MKLabelView model: self counter aspect: #count) render.\x0a\x09(MKButtonView model: self counter aspect: #increase) \x0a\x09\x09label: 'Increase';\x0a\x09\x09render.\x0a\x09(MKInputView model: self counter aspect: #text)\x0a\x09\x09render.\x0a\x09(MKCheckboxView model: self counter aspect: #checked)\x0a\x09\x09render.\x0a\x09(MKButtonView model: self counter aspect: #decrease) \x0a\x09\x09label: 'Decrease';\x0a\x09\x09render",
+source: "build\x0a\x09(MKLabelView model: self counter aspect: #count) render.\x0a\x09(MKButtonView model: self counter aspect: #increase) \x0a\x09\x09label: 'Increase';\x0a\x09\x09render.\x0a\x09(MKInputView model: self counter aspect: #text)\x0a\x09\x09render.\x0a\x09(MKTextAreaView model: self counter aspect: #text)\x0a\x09\x09render.\x0a\x09(MKCheckboxView model: self counter aspect: #checked)\x0a\x09\x09render.\x0a\x09(MKSwitchView model: self counter aspect: #checked)\x0a\x09\x09render.\x0a\x09(MKButtonView model: self counter aspect: #decrease) \x0a\x09\x09label: 'Decrease';\x0a\x09\x09render",
 messageSends: ["render", "model:aspect:", "counter", "label:"],
-referencedClasses: ["MKLabelView", "MKButtonView", "MKInputView", "MKCheckboxView"]
+referencedClasses: ["MKLabelView", "MKButtonView", "MKInputView", "MKTextAreaView", "MKCheckboxView", "MKSwitchView"]
 }),
 smalltalk.MKCounterBuilder);
 

+ 202 - 70
js/Moka-Views.js

@@ -199,7 +199,7 @@ smalltalk.MKButtonView);
 
 
 
-smalltalk.addClass('MKCheckboxView', smalltalk.MKAspectView, [], 'Moka-Views');
+smalltalk.addClass('MKCheckboxView', smalltalk.MKAspectView, ['id'], 'Moka-Views');
 smalltalk.MKCheckboxView.comment="I am a checkbox view. My default controller is `MKCheckboxController`.\x0a\x0aMy controller must answer to `#onToggled:`.\x0a\x0a##API\x0a\x0a- If no `aspect` is provided, the ckeckbox state will always be off.\x0a- use `#label:` to set the label string.";
 smalltalk.addMethod(
 smalltalk.method({
@@ -224,6 +224,22 @@ referencedClasses: []
 }),
 smalltalk.MKCheckboxView);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "cssClass",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+return "mk_checkbox";
+}, function($ctx1) {$ctx1.fill(self,"cssClass",{},smalltalk.MKCheckboxView)})},
+args: [],
+source: "cssClass\x0a\x09^ 'mk_checkbox'",
+messageSends: [],
+referencedClasses: []
+}),
+smalltalk.MKCheckboxView);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "defaultControllerClass",
@@ -241,6 +257,30 @@ referencedClasses: ["MKCheckboxController"]
 }),
 smalltalk.MKCheckboxView);
 
+smalltalk.addMethod(
+smalltalk.method({
+selector: "id",
+category: 'accessing',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $2,$1;
+$2=self["@id"];
+if(($receiver = $2) == nil || $receiver == null){
+self["@id"]=_st((1000000)._atRandom())._asString();
+$1=self["@id"];
+} else {
+$1=$2;
+};
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"id",{},smalltalk.MKCheckboxView)})},
+args: [],
+source: "id\x0a\x09^ id ifNil: [ id := 1000000 atRandom asString ]",
+messageSends: ["ifNil:", "asString", "atRandom"],
+referencedClasses: []
+}),
+smalltalk.MKCheckboxView);
+
 smalltalk.addMethod(
 smalltalk.method({
 selector: "pressed",
@@ -263,93 +303,157 @@ selector: "renderContentOn:",
 category: 'rendering',
 fn: function (html){
 var self=this;
-var checkbox,id;
+var checkbox;
 return smalltalk.withContext(function($ctx1) { 
-var $1,$2,$3,$4,$5;
-id=_st((1000000)._atRandom())._asString();
+var $1,$2,$3,$4,$5,$6,$7;
 $1=_st(html)._input();
 _st($1)._type_("checkbox");
-_st($1)._id_(id);
-$2=_st($1)._onClick_((function(){
+_st($1)._class_(self._cssClass());
+$2=$1;
+$3=self._id();
+$ctx1.sendIdx["id"]=1;
+_st($2)._id_($3);
+$4=_st($1)._onClick_((function(){
 return smalltalk.withContext(function($ctx2) {
 return self._pressed();
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)})}));
-checkbox=$2;
-$3=self._checked();
-if(smalltalk.assert($3)){
+checkbox=$4;
+$5=self._checked();
+if(smalltalk.assert($5)){
 _st(checkbox)._at_put_("checked","checked");
 };
-$4=_st(html)._label();
-_st($4)._for_(id);
-$5=_st($4)._with_((function(){
+$6=_st(html)._label();
+_st($6)._for_(self._id());
+$7=_st($6)._with_((function(){
 return smalltalk.withContext(function($ctx2) {
 return _st(html)._entity_("nbsp");
 }, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)})}));
-return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html,checkbox:checkbox,id:id},smalltalk.MKCheckboxView)})},
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html,checkbox:checkbox},smalltalk.MKCheckboxView)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09| checkbox id |\x0a\x09\x0a\x09 id := 1000000 atRandom asString.\x0a\x09\x0a\x09checkbox := html input\x0a\x09\x09type: 'checkbox';\x0a\x09\x09id: id;\x0a\x09\x09onClick: [ self pressed ].\x0a\x09\x09\x0a\x09self checked ifTrue: [ \x0a\x09\x09checkbox at: 'checked' put: 'checked' ].\x0a\x09\x09\x0a\x09html label\x0a\x09\x09for: id;\x0a\x09\x09with: [ html entity: 'nbsp' ]",
-messageSends: ["asString", "atRandom", "type:", "input", "id:", "onClick:", "pressed", "ifTrue:", "checked", "at:put:", "for:", "label", "with:", "entity:"],
+source: "renderContentOn: html\x0a\x09| checkbox |\x0a\x09\x0a\x09checkbox := html input\x0a\x09\x09type: 'checkbox';\x0a\x09\x09class: self cssClass;\x0a\x09\x09id: self id;\x0a\x09\x09onClick: [ self pressed ].\x0a\x09\x09\x0a\x09self checked ifTrue: [ \x0a\x09\x09checkbox at: 'checked' put: 'checked' ].\x0a\x09\x09\x0a\x09html label\x0a\x09\x09for: self id;\x0a\x09\x09with: [ html entity: 'nbsp' ]",
+messageSends: ["type:", "input", "class:", "cssClass", "id:", "id", "onClick:", "pressed", "ifTrue:", "checked", "at:put:", "for:", "label", "with:", "entity:"],
+referencedClasses: []
+}),
+smalltalk.MKCheckboxView);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "update",
+category: 'events',
+fn: function (){
+var self=this;
+var checkbox;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+checkbox=_st("#".__comma(self._id()))._asJQuery();
+$1=self._checked();
+if(smalltalk.assert($1)){
+_st(checkbox)._attr_put_("checked","checked");
+} else {
+_st(checkbox)._removeAttr_("checked");
+};
+return self}, function($ctx1) {$ctx1.fill(self,"update",{checkbox:checkbox},smalltalk.MKCheckboxView)})},
+args: [],
+source: "update\x0a\x09| checkbox |\x0a\x09checkbox := ('#', self id) asJQuery.\x0a\x09\x0a\x09self checked\x0a\x09\x09ifTrue: [ checkbox attr: 'checked' put: 'checked' ]\x0a\x09\x09ifFalse: [ checkbox removeAttr: 'checked' ]",
+messageSends: ["asJQuery", ",", "id", "ifTrue:ifFalse:", "checked", "attr:put:", "removeAttr:"],
 referencedClasses: []
 }),
 smalltalk.MKCheckboxView);
 
 
 
-smalltalk.addClass('MKInputView', smalltalk.MKAspectView, ['input'], 'Moka-Views');
-smalltalk.MKInputView.comment="I am an input view. My default controller is `MKInputController`.\x0a\x0aMy controller must answer to `#onEnterPressed:`.";
+smalltalk.addClass('MKSwitchView', smalltalk.MKCheckboxView, [], 'Moka-Views');
+smalltalk.MKSwitchView.comment="I am a switch view, similar to a `MKCheckboxView` but displayed as a switch. \x0aMy default controller is `MKCheckboxController`.";
 smalltalk.addMethod(
 smalltalk.method({
-selector: "defaultControllerClass",
-category: 'defaults',
+selector: "cssClass",
+category: 'accessing',
 fn: function (){
 var self=this;
-function $MKInputController(){return smalltalk.MKInputController||(typeof MKInputController=="undefined"?nil:MKInputController)}
 return smalltalk.withContext(function($ctx1) { 
-return $MKInputController();
-}, function($ctx1) {$ctx1.fill(self,"defaultControllerClass",{},smalltalk.MKInputView)})},
+return "mk_switch";
+}, function($ctx1) {$ctx1.fill(self,"cssClass",{},smalltalk.MKSwitchView)})},
 args: [],
-source: "defaultControllerClass\x0a\x09^ MKInputController",
+source: "cssClass\x0a\x09^ 'mk_switch'",
 messageSends: [],
-referencedClasses: ["MKInputController"]
+referencedClasses: []
 }),
-smalltalk.MKInputView);
+smalltalk.MKSwitchView);
+
+
 
+smalltalk.addClass('MKLabelView', smalltalk.MKAspectView, ['input'], 'Moka-Views');
+smalltalk.MKLabelView.comment="I am an label view. I display a `String`.";
 smalltalk.addMethod(
 smalltalk.method({
-selector: "enterPressed",
-category: 'events',
+selector: "defaultControllerClass",
+category: 'defaults',
 fn: function (){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(self._controller())._onEnterPressed_(self._value());
-return self}, function($ctx1) {$ctx1.fill(self,"enterPressed",{},smalltalk.MKInputView)})},
+var $1;
+$1=smalltalk.MKLabelView.superclass.fn.prototype._defaultControllerClass.apply(_st(self), []);
+return $1;
+}, function($ctx1) {$ctx1.fill(self,"defaultControllerClass",{},smalltalk.MKLabelView)})},
 args: [],
-source: "enterPressed\x0a\x09self controller onEnterPressed: self value",
-messageSends: ["onEnterPressed:", "controller", "value"],
+source: "defaultControllerClass\x0a\x09^ super defaultControllerClass",
+messageSends: ["defaultControllerClass"],
 referencedClasses: []
 }),
-smalltalk.MKInputView);
+smalltalk.MKLabelView);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "renderContentOn:",
+category: 'rendering',
+fn: function (html){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+_st(_st(html)._span())._with_(self._aspectValue());
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.MKLabelView)})},
+args: ["html"],
+source: "renderContentOn: html\x0a\x09html span with: self aspectValue",
+messageSends: ["with:", "span", "aspectValue"],
+referencedClasses: []
+}),
+smalltalk.MKLabelView);
+
+
 
+smalltalk.addClass('MKTextAreaView', smalltalk.MKAspectView, ['input'], 'Moka-Views');
+smalltalk.MKTextAreaView.comment="I am an text area view. My default controller is `MKAnyKeyInputController`.\x0a\x0aMy controller must answer to `#onKeyPressed:`.";
 smalltalk.addMethod(
 smalltalk.method({
-selector: "keyDown:",
+selector: "defaultControllerClass",
+category: 'defaults',
+fn: function (){
+var self=this;
+function $MKAnyKeyInputController(){return smalltalk.MKAnyKeyInputController||(typeof MKAnyKeyInputController=="undefined"?nil:MKAnyKeyInputController)}
+return smalltalk.withContext(function($ctx1) { 
+return $MKAnyKeyInputController();
+}, function($ctx1) {$ctx1.fill(self,"defaultControllerClass",{},smalltalk.MKTextAreaView)})},
+args: [],
+source: "defaultControllerClass\x0a\x09^ MKAnyKeyInputController",
+messageSends: [],
+referencedClasses: ["MKAnyKeyInputController"]
+}),
+smalltalk.MKTextAreaView);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "keyUp:",
 category: 'events',
 fn: function (anEvent){
 var self=this;
-function $String(){return smalltalk.String||(typeof String=="undefined"?nil:String)}
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=_st(_st(anEvent)._keyCode()).__eq(_st(_st($String())._cr())._asciiValue());
-if(smalltalk.assert($1)){
-self._enterPressed();
-};
-return self}, function($ctx1) {$ctx1.fill(self,"keyDown:",{anEvent:anEvent},smalltalk.MKInputView)})},
+_st(self._controller())._onKeyPressed_(anEvent);
+return self}, function($ctx1) {$ctx1.fill(self,"keyUp:",{anEvent:anEvent},smalltalk.MKTextAreaView)})},
 args: ["anEvent"],
-source: "keyDown: anEvent\x0a\x09anEvent keyCode = String cr asciiValue ifTrue: [\x0a\x09\x09self enterPressed ]",
-messageSends: ["ifTrue:", "=", "keyCode", "asciiValue", "cr", "enterPressed"],
-referencedClasses: ["String"]
+source: "keyUp: anEvent\x0a\x09self controller onKeyPressed: anEvent",
+messageSends: ["onKeyPressed:", "controller"],
+referencedClasses: []
 }),
-smalltalk.MKInputView);
+smalltalk.MKTextAreaView);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -359,21 +463,42 @@ fn: function (html){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
 var $1,$2;
-$1=_st(html)._input();
-_st($1)._value_(self._aspectValue());
-_st($1)._onKeyDown_((function(event){
+$1=_st(html)._textarea();
+_st($1)._with_(self._aspectValue());
+$2=_st($1)._onKeyUp_((function(event){
 return smalltalk.withContext(function($ctx2) {
-return self._keyDown_(event);
+return self._keyUp_(event);
 }, function($ctx2) {$ctx2.fillBlock({event:event},$ctx1,1)})}));
-$2=_st($1)._yourself();
 self["@input"]=$2;
-return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.MKInputView)})},
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.MKTextAreaView)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09input := html input\x0a\x09\x09value: self aspectValue;\x0a\x09\x09onKeyDown: [ :event |\x0a\x09\x09\x09self keyDown: event ];\x0a\x09\x09yourself",
-messageSends: ["value:", "input", "aspectValue", "onKeyDown:", "keyDown:", "yourself"],
+source: "renderContentOn: html\x0a\x09input := html textarea \x0a\x09\x09with: self aspectValue;\x0a\x09\x09onKeyUp: [ :event | self keyUp: event ]",
+messageSends: ["with:", "textarea", "aspectValue", "onKeyUp:", "keyUp:"],
 referencedClasses: []
 }),
-smalltalk.MKInputView);
+smalltalk.MKTextAreaView);
+
+smalltalk.addMethod(
+smalltalk.method({
+selector: "update",
+category: 'updating',
+fn: function (){
+var self=this;
+return smalltalk.withContext(function($ctx1) { 
+var $1;
+$1=self["@input"];
+if(($receiver = $1) == nil || $receiver == null){
+$1;
+} else {
+_st(_st(self["@input"])._asJQuery())._val_(self._aspectValue());
+};
+return self}, function($ctx1) {$ctx1.fill(self,"update",{},smalltalk.MKTextAreaView)})},
+args: [],
+source: "update\x0a\x09input ifNotNil: [ input asJQuery val: self aspectValue ]",
+messageSends: ["ifNotNil:", "val:", "asJQuery", "aspectValue"],
+referencedClasses: []
+}),
+smalltalk.MKTextAreaView);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -385,35 +510,34 @@ return smalltalk.withContext(function($ctx1) {
 var $1;
 $1=_st(_st(self["@input"])._asJQuery())._val();
 return $1;
-}, function($ctx1) {$ctx1.fill(self,"value",{},smalltalk.MKInputView)})},
+}, function($ctx1) {$ctx1.fill(self,"value",{},smalltalk.MKTextAreaView)})},
 args: [],
 source: "value\x0a\x09^ input asJQuery val",
 messageSends: ["val", "asJQuery"],
 referencedClasses: []
 }),
-smalltalk.MKInputView);
+smalltalk.MKTextAreaView);
 
 
 
-smalltalk.addClass('MKLabelView', smalltalk.MKAspectView, ['input'], 'Moka-Views');
-smalltalk.MKLabelView.comment="I am an label view. I display a `String`.";
+smalltalk.addClass('MKInputView', smalltalk.MKTextAreaView, [], 'Moka-Views');
+smalltalk.MKInputView.comment="I am an input view. My default controller is `MKEnterInputController`.\x0a\x0aMy controller must answer to `#onKeyPressed:`.";
 smalltalk.addMethod(
 smalltalk.method({
 selector: "defaultControllerClass",
 category: 'defaults',
 fn: function (){
 var self=this;
+function $MKEnterInputController(){return smalltalk.MKEnterInputController||(typeof MKEnterInputController=="undefined"?nil:MKEnterInputController)}
 return smalltalk.withContext(function($ctx1) { 
-var $1;
-$1=smalltalk.MKLabelView.superclass.fn.prototype._defaultControllerClass.apply(_st(self), []);
-return $1;
-}, function($ctx1) {$ctx1.fill(self,"defaultControllerClass",{},smalltalk.MKLabelView)})},
+return $MKEnterInputController();
+}, function($ctx1) {$ctx1.fill(self,"defaultControllerClass",{},smalltalk.MKInputView)})},
 args: [],
-source: "defaultControllerClass\x0a\x09^ super defaultControllerClass",
-messageSends: ["defaultControllerClass"],
-referencedClasses: []
+source: "defaultControllerClass\x0a\x09^ MKEnterInputController",
+messageSends: [],
+referencedClasses: ["MKEnterInputController"]
 }),
-smalltalk.MKLabelView);
+smalltalk.MKInputView);
 
 smalltalk.addMethod(
 smalltalk.method({
@@ -422,14 +546,22 @@ category: 'rendering',
 fn: function (html){
 var self=this;
 return smalltalk.withContext(function($ctx1) { 
-_st(_st(html)._span())._with_(self._aspectValue());
-return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.MKLabelView)})},
+var $1,$2;
+$1=_st(html)._input();
+_st($1)._value_(self._aspectValue());
+_st($1)._onKeyUp_((function(event){
+return smalltalk.withContext(function($ctx2) {
+return self._keyUp_(event);
+}, function($ctx2) {$ctx2.fillBlock({event:event},$ctx1,1)})}));
+$2=_st($1)._yourself();
+self["@input"]=$2;
+return self}, function($ctx1) {$ctx1.fill(self,"renderContentOn:",{html:html},smalltalk.MKInputView)})},
 args: ["html"],
-source: "renderContentOn: html\x0a\x09html span with: self aspectValue",
-messageSends: ["with:", "span", "aspectValue"],
+source: "renderContentOn: html\x0a\x09input := html input\x0a\x09\x09value: self aspectValue;\x0a\x09\x09onKeyUp: [ :event |\x0a\x09\x09\x09self keyUp: event ];\x0a\x09\x09yourself",
+messageSends: ["value:", "input", "aspectValue", "onKeyUp:", "keyUp:", "yourself"],
 referencedClasses: []
 }),
-smalltalk.MKLabelView);
+smalltalk.MKInputView);
 
 
 });

+ 30 - 12
st/Moka-Controllers.st

@@ -1,4 +1,34 @@
 Smalltalk current createPackage: 'Moka-Controllers'!
+MKAspectController subclass: #MKAnyKeyInputController
+	instanceVariableNames: ''
+	package: 'Moka-Controllers'!
+
+!MKAnyKeyInputController methodsFor: 'accessing]'!
+
+inputText
+	^ self view value
+! !
+
+!MKAnyKeyInputController methodsFor: 'actions'!
+
+onKeyPressed: anEvent
+	self performActionWith: self inputText
+! !
+
+MKAnyKeyInputController subclass: #MKEnterInputController
+	instanceVariableNames: ''
+	package: 'Moka-Controllers'!
+!MKEnterInputController commentStamp!
+I am the default controller for `MKInputView`. 
+Actions are performed on 'enter' key press.!
+
+!MKEnterInputController methodsFor: 'actions'!
+
+onKeyPressed: anEvent
+	anEvent keyCode = String cr asciiValue ifTrue: [
+		super onKeyPressed: anEvent ]
+! !
+
 MKAspectController subclass: #MKButtonController
 	instanceVariableNames: ''
 	package: 'Moka-Controllers'!
@@ -23,15 +53,3 @@ onToggled: aBoolean
 	self performActionWith: aBoolean
 ! !
 
-MKAspectController subclass: #MKInputController
-	instanceVariableNames: ''
-	package: 'Moka-Controllers'!
-!MKInputController commentStamp!
-I am the default controller for `MKInputView`.!
-
-!MKInputController methodsFor: 'actions'!
-
-onEnterPressed: aString
-	self performActionWith: aString
-! !
-

+ 4 - 0
st/Moka-Examples.st

@@ -12,8 +12,12 @@ build
 		render.
 	(MKInputView model: self counter aspect: #text)
 		render.
+	(MKTextAreaView model: self counter aspect: #text)
+		render.
 	(MKCheckboxView model: self counter aspect: #checked)
 		render.
+	(MKSwitchView model: self counter aspect: #checked)
+		render.
 	(MKButtonView model: self counter aspect: #decrease) 
 		label: 'Decrease';
 		render

+ 87 - 35
st/Moka-Views.st

@@ -68,7 +68,7 @@ isDefault
 ! !
 
 MKAspectView subclass: #MKCheckboxView
-	instanceVariableNames: ''
+	instanceVariableNames: 'id'
 	package: 'Moka-Views'!
 !MKCheckboxView commentStamp!
 I am a checkbox view. My default controller is `MKCheckboxController`.
@@ -84,6 +84,14 @@ My controller must answer to `#onToggled:`.
 
 checked
 	^ self aspectValue ifNil: [ false ]
+!
+
+cssClass
+	^ 'mk_checkbox'
+!
+
+id
+	^ id ifNil: [ id := 1000000 atRandom asString ]
 ! !
 
 !MKCheckboxView methodsFor: 'defaults'!
@@ -96,84 +104,128 @@ defaultControllerClass
 
 pressed
 	self controller onToggled: self checked not
+!
+
+update
+	| checkbox |
+	checkbox := ('#', self id) asJQuery.
+	
+	self checked
+		ifTrue: [ checkbox attr: 'checked' put: 'checked' ]
+		ifFalse: [ checkbox removeAttr: 'checked' ]
 ! !
 
 !MKCheckboxView methodsFor: 'rendering'!
 
 renderContentOn: html
-	| checkbox id |
-	
-	 id := 1000000 atRandom asString.
+	| checkbox |
 	
 	checkbox := html input
 		type: 'checkbox';
-		id: id;
+		class: self cssClass;
+		id: self id;
 		onClick: [ self pressed ].
 		
 	self checked ifTrue: [ 
 		checkbox at: 'checked' put: 'checked' ].
 		
 	html label
-		for: id;
+		for: self id;
 		with: [ html entity: 'nbsp' ]
 ! !
 
-MKAspectView subclass: #MKInputView
+MKCheckboxView subclass: #MKSwitchView
+	instanceVariableNames: ''
+	package: 'Moka-Views'!
+!MKSwitchView commentStamp!
+I am a switch view, similar to a `MKCheckboxView` but displayed as a switch. 
+My default controller is `MKCheckboxController`.!
+
+!MKSwitchView methodsFor: 'accessing'!
+
+cssClass
+	^ 'mk_switch'
+! !
+
+MKAspectView subclass: #MKLabelView
 	instanceVariableNames: 'input'
 	package: 'Moka-Views'!
-!MKInputView commentStamp!
-I am an input view. My default controller is `MKInputController`.
+!MKLabelView commentStamp!
+I am an label view. I display a `String`.!
+
+!MKLabelView methodsFor: 'defaults'!
 
-My controller must answer to `#onEnterPressed:`.!
+defaultControllerClass
+	^ super defaultControllerClass
+! !
+
+!MKLabelView methodsFor: 'rendering'!
 
-!MKInputView methodsFor: 'accessing'!
+renderContentOn: html
+	html span with: self aspectValue
+! !
+
+MKAspectView subclass: #MKTextAreaView
+	instanceVariableNames: 'input'
+	package: 'Moka-Views'!
+!MKTextAreaView commentStamp!
+I am an text area view. My default controller is `MKAnyKeyInputController`.
+
+My controller must answer to `#onKeyPressed:`.!
+
+!MKTextAreaView methodsFor: 'accessing'!
 
 value
 	^ input asJQuery val
 ! !
 
-!MKInputView methodsFor: 'defaults'!
+!MKTextAreaView methodsFor: 'defaults'!
 
 defaultControllerClass
-	^ MKInputController
+	^ MKAnyKeyInputController
 ! !
 
-!MKInputView methodsFor: 'events'!
-
-enterPressed
-	self controller onEnterPressed: self value
-!
+!MKTextAreaView methodsFor: 'events'!
 
-keyDown: anEvent
-	anEvent keyCode = String cr asciiValue ifTrue: [
-		self enterPressed ]
+keyUp: anEvent
+	self controller onKeyPressed: anEvent
 ! !
 
-!MKInputView methodsFor: 'rendering'!
+!MKTextAreaView methodsFor: 'rendering'!
 
 renderContentOn: html
-	input := html input
-		value: self aspectValue;
-		onKeyDown: [ :event |
-			self keyDown: event ];
-		yourself
+	input := html textarea 
+		with: self aspectValue;
+		onKeyUp: [ :event | self keyUp: event ]
 ! !
 
-MKAspectView subclass: #MKLabelView
-	instanceVariableNames: 'input'
+!MKTextAreaView methodsFor: 'updating'!
+
+update
+	input ifNotNil: [ input asJQuery val: self aspectValue ]
+! !
+
+MKTextAreaView subclass: #MKInputView
+	instanceVariableNames: ''
 	package: 'Moka-Views'!
-!MKLabelView commentStamp!
-I am an label view. I display a `String`.!
+!MKInputView commentStamp!
+I am an input view. My default controller is `MKEnterInputController`.
 
-!MKLabelView methodsFor: 'defaults'!
+My controller must answer to `#onKeyPressed:`.!
+
+!MKInputView methodsFor: 'defaults'!
 
 defaultControllerClass
-	^ super defaultControllerClass
+	^ MKEnterInputController
 ! !
 
-!MKLabelView methodsFor: 'rendering'!
+!MKInputView methodsFor: 'rendering'!
 
 renderContentOn: html
-	html span with: self aspectValue
+	input := html input
+		value: self aspectValue;
+		onKeyUp: [ :event |
+			self keyUp: event ];
+		yourself
 ! !