Browse Source

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

Nicolas Petton 10 năm trước cách đây
mục cha
commit
811b7e13eb
10 tập tin đã thay đổi với 517 bổ sung178 xóa
  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
 ! !