You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@royale.apache.org by ca...@apache.org on 2018/08/24 19:35:10 UTC

[royale-asjs] branch develop updated: Jewel NumericStepper first round

This is an automated email from the ASF dual-hosted git repository.

carlosrovira pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/royale-asjs.git


The following commit(s) were added to refs/heads/develop by this push:
     new 49926ad  Jewel NumericStepper first round
49926ad is described below

commit 49926add111921696e7fb14d8772add304f53c74
Author: Carlos Rovira <ca...@apache.org>
AuthorDate: Fri Aug 24 21:35:04 2018 +0200

    Jewel NumericStepper first round
---
 .../src/main/royale/ButtonPlayGround.mxml          |   2 +
 .../projects/Jewel/src/main/resources/defaults.css |  24 ++
 .../Jewel/src/main/resources/jewel-manifest.xml    |   2 +
 .../projects/Jewel/src/main/royale/JewelClasses.as |   3 +
 .../main/royale/org/apache/royale/jewel/Button.as  |   2 +-
 .../org/apache/royale/jewel/NumericStepper.as      | 192 ++++++++++++++++
 .../main/royale/org/apache/royale/jewel/Spinner.as | 182 ++++++++++++++++
 .../beads/controllers/SpinnerMouseController.as    | 131 +++++++++++
 .../jewel/beads/controls/spinner/ISpinnerView.as   |  69 ++++++
 .../royale/jewel/beads/views/NumericStepperView.as | 242 +++++++++++++++++++++
 .../apache/royale/jewel/beads/views/SpinnerView.as | 212 ++++++++++++++++++
 .../src/main/sass/components/_numericstepper.sass  |  59 +++++
 .../projects/Jewel/src/main/sass/defaults.sass     |   1 +
 .../JewelTheme/src/main/resources/defaults.css     |  28 +++
 .../sass/components-primary/_numericstepper.sass   |  61 ++++++
 .../themes/JewelTheme/src/main/sass/defaults.sass  |   1 +
 16 files changed, 1210 insertions(+), 1 deletion(-)

diff --git a/examples/royale/JewelExample/src/main/royale/ButtonPlayGround.mxml b/examples/royale/JewelExample/src/main/royale/ButtonPlayGround.mxml
index e99c23e..e1eeb42 100644
--- a/examples/royale/JewelExample/src/main/royale/ButtonPlayGround.mxml
+++ b/examples/royale/JewelExample/src/main/royale/ButtonPlayGround.mxml
@@ -97,6 +97,8 @@ limitations under the License.
 						<js:FontIcon text="{MaterialIconType.FACE}" material="true"/>
 					</j:icon>
 				</j:IconButton>
+
+				<j:NumericStepper/>
 			</j:Card>
 		</j:GridCell>
 	</j:Grid>
diff --git a/frameworks/projects/Jewel/src/main/resources/defaults.css b/frameworks/projects/Jewel/src/main/resources/defaults.css
index 77e4ec2..20bf8c3 100644
--- a/frameworks/projects/Jewel/src/main/resources/defaults.css
+++ b/frameworks/projects/Jewel/src/main/resources/defaults.css
@@ -2732,6 +2732,30 @@ j|Navigation {
     IContentView: ClassReference("org.apache.royale.html.supportClasses.DataGroup");
   }
 }
+.jewel.numericstepper .jewel.textinput {
+  display: inline-flex;
+}
+
+j|NumericStepper {
+  IBeadModel: ClassReference("org.apache.royale.jewel.beads.models.RangeModel");
+  IBeadView: ClassReference("org.apache.royale.jewel.beads.views.NumericStepperView");
+}
+
+.jewel.spinner {
+  display: inline-flex;
+  flex-direction: column;
+  vertical-align: middle;
+}
+.jewel.spinner .jewel.button {
+  display: flex;
+}
+
+j|Spinner {
+  IBeadModel: ClassReference("org.apache.royale.jewel.beads.models.RangeModel");
+  IBeadView: ClassReference("org.apache.royale.jewel.beads.views.SpinnerView");
+  IBeadController: ClassReference("org.apache.royale.jewel.beads.controllers.SpinnerMouseController");
+}
+
 .jewel.radiobutton {
   display: inline-flex;
   position: relative;
diff --git a/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml b/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml
index 471cd89..bffb938 100644
--- a/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml
+++ b/frameworks/projects/Jewel/src/main/resources/jewel-manifest.xml
@@ -47,6 +47,8 @@
     <component id="TextInput" class="org.apache.royale.jewel.TextInput"/>
     <component id="IconTextInput" class="org.apache.royale.jewel.IconTextInput"/>
     <component id="TextArea" class="org.apache.royale.jewel.TextArea"/>
+    <component id="Spinner" class="org.apache.royale.jewel.Spinner" />
+    <component id="NumericStepper" class="org.apache.royale.jewel.NumericStepper" />
     <component id="Slider" class="org.apache.royale.jewel.Slider"/>
     <component id="RadioButton" class="org.apache.royale.jewel.RadioButton"/>
     <component id="CheckBox" class="org.apache.royale.jewel.CheckBox"/>
diff --git a/frameworks/projects/Jewel/src/main/royale/JewelClasses.as b/frameworks/projects/Jewel/src/main/royale/JewelClasses.as
index d9882ce..2425926 100644
--- a/frameworks/projects/Jewel/src/main/royale/JewelClasses.as
+++ b/frameworks/projects/Jewel/src/main/royale/JewelClasses.as
@@ -36,6 +36,7 @@ package
         import org.apache.royale.jewel.beads.models.DateChooserModel; DateChooserModel;
         import org.apache.royale.jewel.beads.models.DataProviderModel; DataProviderModel;
 
+        import org.apache.royale.jewel.beads.controllers.SpinnerMouseController; SpinnerMouseController;
         import org.apache.royale.jewel.beads.controllers.SliderMouseController; SliderMouseController;
         import org.apache.royale.jewel.beads.controllers.DateChooserMouseController; DateChooserMouseController;
         import org.apache.royale.jewel.beads.controllers.DateFieldMouseController; DateFieldMouseController;
@@ -45,6 +46,8 @@ package
         import org.apache.royale.jewel.beads.controllers.TableCellSelectionMouseController; TableCellSelectionMouseController;
         
         import org.apache.royale.jewel.beads.views.ImageView; ImageView;
+        import org.apache.royale.jewel.beads.views.SpinnerView; SpinnerView;
+        import org.apache.royale.jewel.beads.views.NumericStepperView; NumericStepperView;
         import org.apache.royale.jewel.beads.views.SliderView; SliderView;
         import org.apache.royale.jewel.beads.views.AlertView; AlertView;
         import org.apache.royale.jewel.beads.views.TitleBarView; TitleBarView;
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Button.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Button.as
index fb59004..1d8a676 100644
--- a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Button.as
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Button.as
@@ -452,7 +452,7 @@ package org.apache.royale.jewel
          */
         override protected function createElement():WrappedHTMLElement
         {
-			addElementToWrapper(this,'button');
+			addElementToWrapper(this, 'button');
             element.setAttribute('type', 'button');
             /* AJH comment out until we figure out why it is needed
             if (org.apache.royale.core.ValuesManager.valuesImpl.getValue) {
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/NumericStepper.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/NumericStepper.as
new file mode 100644
index 0000000..f88f2c6
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/NumericStepper.as
@@ -0,0 +1,192 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.jewel
+{
+	import org.apache.royale.core.IRangeModel;
+	import org.apache.royale.core.StyledUIBase;
+    COMPILE::JS
+    {
+        import goog.events;
+        import org.apache.royale.core.IBead;
+        import org.apache.royale.core.WrappedHTMLElement;
+        import org.apache.royale.events.IEventDispatcher;
+        import org.apache.royale.html.util.addElementToWrapper;
+    }
+
+	[Event(name="valueChange", type="org.apache.royale.events.Event")]
+
+	/**
+	 *  The NumericStepper class is a component that displays a numeric
+	 *  value and up/down controls (using a org.apache.royale.jewel.Spinner) to
+	 *  increase and decrease the value by specific amounts. The NumericStepper uses the following beads:
+	 *
+	 *  org.apache.royale.core.IBeadModel: the data model for the component of type org.apache.royale.core.IRangeModel.
+	 *  org.apache.royale.core.IBeadView: constructs the parts of the component.
+	 *  org.apache.royale.core.IBeadController: handles the input events.
+	 *
+     *  @toplevel
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10.2
+	 *  @playerversion AIR 2.6
+	 *  @productversion Royale 0.9.3
+	 */
+	public class NumericStepper extends StyledUIBase
+	{
+		/**
+		 *  constructor.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		public function NumericStepper()
+		{
+			super();
+			typeNames = 'jewel numericstepper';
+		}
+
+        [Bindable("valueChange")]
+		/**
+		 *  The current value of the control.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get value():Number
+		{
+			return IRangeModel(model).value;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set value(newValue:Number):void
+		{
+			IRangeModel(model).value = newValue;
+		}
+
+		/**
+		 *  The minimum value the control will display.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get minimum():Number
+		{
+			return IRangeModel(model).minimum;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set minimum(value:Number):void
+		{
+			IRangeModel(model).minimum = value;
+		}
+
+		/**
+		 *  The maximum value the control will display.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get maximum():Number
+		{
+			return IRangeModel(model).maximum;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set maximum(value:Number):void
+		{
+			IRangeModel(model).maximum = value;
+		}
+
+		/**
+		 *  The amount to increase or descrease the value. The value
+		 *  will not exceed the minimum or maximum value. The final
+		 *  value is affected by the snapInterval.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get stepSize():Number
+		{
+			return IRangeModel(model).stepSize;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set stepSize(value:Number):void
+		{
+			IRangeModel(model).stepSize = value;
+		}
+
+		/**
+		 *  The modulus for the value. If this property is set,
+		 *  the value displayed with a muliple of the snapInterval.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get snapInterval():Number
+		{
+			return IRangeModel(model).snapInterval;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set snapInterval(value:Number):void
+		{
+			IRangeModel(model).snapInterval = value;
+		}
+
+        COMPILE::JS
+        private var input:TextInput;
+
+        COMPILE::JS
+        private var spinner:Spinner;
+
+        /**
+         * @royaleignorecoercion org.apache.royale.core.WrappedHTMLElement
+         */
+        COMPILE::JS
+        override protected function createElement():WrappedHTMLElement
+        {
+			addElementToWrapper(this, 'div');
+			positioner = element;
+			return element;
+        }
+
+	}
+}
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Spinner.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Spinner.as
new file mode 100644
index 0000000..93857ad
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/Spinner.as
@@ -0,0 +1,182 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.jewel
+{
+	import org.apache.royale.core.IRangeModel;
+	import org.apache.royale.core.StyledUIBase;
+
+    COMPILE::JS
+    {
+        import org.apache.royale.core.WrappedHTMLElement;
+		import org.apache.royale.html.util.addElementToWrapper;
+    }
+
+	[Event(name="valueChange", type="org.apache.royale.events.Event")]
+
+	/**
+	 *  The Spinner class is a component that displays a control for incrementing a value
+	 *  and a control for decrementing a value. The org.apache.royale.jewel.NumericStepper
+	 *  uses a Spinner as part of the component. Spinner uses the following beads:
+	 *
+	 *  org.apache.royale.core.IBeadModel: an IRangeModel to hold the properties.
+	 *  org.apache.royale.core.IBeadView:  the bead that constructs the visual parts of the Spinner.
+	 *  org.apache.royale.core.IBeadController: a bead that handles the input events.
+	 *
+     *  @toplevel
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10.2
+	 *  @playerversion AIR 2.6
+	 *  @productversion Royale 0.9.3
+	 */
+	public class Spinner extends StyledUIBase
+	{
+		/**
+		 *  constructor.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		public function Spinner()
+		{
+			super();
+			typeNames = "jewel spinner";
+		}
+
+		/**
+		 *  The current value of the Spinner.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get value():Number
+		{
+			return IRangeModel(model).value;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set value(newValue:Number):void
+		{
+			IRangeModel(model).value = newValue;
+		}
+
+		/**
+		 *  The minimum value of the Spinner.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get minimum():Number
+		{
+			return IRangeModel(model).minimum;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set minimum(value:Number):void
+		{
+			IRangeModel(model).minimum = value;
+		}
+
+		/**
+		 *  The maximum value of the Spinner.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get maximum():Number
+		{
+			return IRangeModel(model).maximum;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set maximum(value:Number):void
+		{
+			IRangeModel(model).maximum = value;
+		}
+
+		/**
+		 *  The modulus for the value. If this property is set,
+		 *  the value displayed with a muliple of the snapInterval.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get snapInterval():Number
+		{
+			return IRangeModel(model).snapInterval;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set snapInterval(value:Number):void
+		{
+			IRangeModel(model).snapInterval = value;
+		}
+
+		/**
+		 *  The amount to increase or descrease the value. The value
+		 *  will not exceed the minimum or maximum value. The final
+		 *  value is affected by the snapInterval.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function get stepSize():Number
+		{
+			return IRangeModel(model).stepSize;
+		}
+		/**
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set stepSize(value:Number):void
+		{
+			IRangeModel(model).stepSize = value;
+		}
+
+        /**
+         * @royaleignorecoercion org.apache.royale.core.WrappedHTMLElement
+         */
+        COMPILE::JS
+        override protected function createElement():WrappedHTMLElement
+        {
+			addElementToWrapper(this,'div');
+            positioner = element;
+			return element;
+        }
+	}
+}
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/controllers/SpinnerMouseController.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/controllers/SpinnerMouseController.as
new file mode 100644
index 0000000..a7aa24f
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/controllers/SpinnerMouseController.as
@@ -0,0 +1,131 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.jewel.beads.controllers
+{
+	import org.apache.royale.core.IBeadController;
+	import org.apache.royale.core.IRangeModel;
+	import org.apache.royale.core.IStrand;
+	import org.apache.royale.core.UIBase;
+	import org.apache.royale.events.IEventDispatcher;
+	import org.apache.royale.events.MouseEvent;
+	import org.apache.royale.events.ValueChangeEvent;
+	import org.apache.royale.jewel.beads.controls.spinner.ISpinnerView;
+    COMPILE::JS
+    {
+        import org.apache.royale.jewel.Spinner;
+        import org.apache.royale.jewel.Button;
+        import goog.events;
+        import goog.events.EventType;
+    }
+
+	/**
+	 *  The SpinnerMouseController class bead handles mouse events on the
+	 *  org.apache.royale.jewel.Spinner's component buttons, changing the
+	 *  value of the Spinner.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10.2
+	 *  @playerversion AIR 2.6
+	 *  @productversion Royale 0.9.3
+	 */
+	public class SpinnerMouseController implements IBeadController
+	{
+		/**
+		 *  constructor.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		public function SpinnerMouseController()
+		{
+		}
+
+		private var rangeModel:IRangeModel;
+
+		private var _strand:IStrand;
+
+		/**
+		 *  @copy org.apache.royale.core.IBead#strand
+		 *
+		 *  @royaleignorecoercion org.apache.royale.jewel.Spinner
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.jewel.beads.controls.spinner.ISpinnerView
+		 *  @royaleignorecoercion org.apache.royale.core.UIBase
+		 *  @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		public function set strand(value:IStrand):void
+		{
+			_strand = value;
+
+			rangeModel = UIBase(value).model as IRangeModel;
+
+            COMPILE::SWF
+            {
+                var spinnerBead:ISpinnerView = value.getBeadByType(ISpinnerView) as ISpinnerView;
+                spinnerBead.decrement.addEventListener(MouseEvent.CLICK, decrementClickHandler);
+                spinnerBead.decrement.addEventListener("buttonRepeat", decrementClickHandler);
+                spinnerBead.increment.addEventListener(MouseEvent.CLICK, incrementClickHandler);
+                spinnerBead.increment.addEventListener("buttonRepeat", incrementClickHandler);
+            }
+
+            COMPILE::JS
+            {
+            	var spinnerBead:ISpinnerView = value.getBeadByType(ISpinnerView) as ISpinnerView;
+
+                var incrementButton:Button = spinnerBead.increment;
+                var decrementButton:Button = spinnerBead.decrement;
+
+                goog.events.listen(incrementButton.element, goog.events.EventType.CLICK,
+                    incrementClickHandler);
+
+                goog.events.listen(decrementButton.element, goog.events.EventType.CLICK,
+                    decrementClickHandler);
+            }
+		}
+
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.events.IEventDispatcher
+		 */
+		private function decrementClickHandler( event:org.apache.royale.events.MouseEvent ) : void
+		{
+			var oldValue:Number = rangeModel.value;
+			rangeModel.value = Math.max(rangeModel.minimum, rangeModel.value - rangeModel.stepSize);
+			var vce:ValueChangeEvent = ValueChangeEvent.createUpdateEvent(_strand, "value", oldValue, rangeModel.value);
+			IEventDispatcher(_strand).dispatchEvent(vce);
+		}
+
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.events.IEventDispatcher
+		 */
+		private function incrementClickHandler( event:org.apache.royale.events.MouseEvent ) : void
+		{
+			var oldValue:Number = rangeModel.value;
+			rangeModel.value = Math.min(rangeModel.maximum, rangeModel.value + rangeModel.stepSize);
+			var vce:ValueChangeEvent = ValueChangeEvent.createUpdateEvent(_strand, "value", oldValue, rangeModel.value);
+			IEventDispatcher(_strand).dispatchEvent(vce);
+		}
+	}
+}
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/controls/spinner/ISpinnerView.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/controls/spinner/ISpinnerView.as
new file mode 100644
index 0000000..f6c2224
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/controls/spinner/ISpinnerView.as
@@ -0,0 +1,69 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.jewel.beads.controls.spinner
+{
+    COMPILE::SWF
+    {
+        import flash.display.DisplayObject;
+    }
+    COMPILE::JS
+    {
+    	import org.apache.royale.jewel.Button;
+    }
+
+	import org.apache.royale.core.IBead;
+
+	/**
+	 *  The ISpinnerView interface provides the protocol for any bead that
+	 *  creates the visual parts for a org.apache.royale.jewel.Spinner control.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10.2
+	 *  @playerversion AIR 2.6
+	 *  @productversion Royale 0.9.3
+	 */
+	public interface ISpinnerView extends IBead
+	{
+		/**
+		 *  The component used to increment the org.apache.royale.jewel.Spinner value.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+        COMPILE::SWF
+		function get increment():DisplayObject;
+		COMPILE::JS
+		function get increment():Button;
+
+		/**
+		 *  The component used to decrement the org.apache.royale.jewel.Spinner value.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+        COMPILE::SWF
+		function get decrement():DisplayObject;
+		COMPILE::JS
+		function get decrement():Button;
+	}
+}
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/NumericStepperView.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/NumericStepperView.as
new file mode 100644
index 0000000..24960ce
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/NumericStepperView.as
@@ -0,0 +1,242 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.jewel.beads.views
+{
+    import org.apache.royale.core.BeadViewBase;
+	import org.apache.royale.core.IBead;
+	import org.apache.royale.core.IBeadView;
+	import org.apache.royale.core.ILayoutChild;
+    import org.apache.royale.core.IParent;
+	import org.apache.royale.core.IParentIUIBase;
+	import org.apache.royale.core.IRangeModel;
+	import org.apache.royale.core.IStrand;
+    import org.apache.royale.core.IUIBase;
+    import org.apache.royale.core.UIBase;
+	import org.apache.royale.events.Event;
+	import org.apache.royale.events.ValueChangeEvent
+	import org.apache.royale.events.IEventDispatcher;
+    import org.apache.royale.jewel.Label;
+	import org.apache.royale.jewel.Spinner;
+	import org.apache.royale.jewel.TextInput;
+	
+	/**
+	 *  The NumericStepperView class creates the visual elements of the 
+	 *  org.apache.royale.jewel.NumericStepper component. A NumberStepper consists of a 
+	 *  org.apache.royale.jewel.TextInput component to display the value and a 
+	 *  org.apache.royale.jewel.Spinner to change the value.
+	 *  
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10.2
+	 *  @playerversion AIR 2.6
+	 *  @productversion Royale 0.9.3
+	 */
+	public class NumericStepperView extends BeadViewBase implements IBeadView
+	{
+		/**
+		 *  constructor.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		public function NumericStepperView()
+		{
+		}
+		
+		private var label:Label;
+		private var input:TextInput;
+		private var spinner:Spinner;
+		
+		/**
+		 *  @copy org.apache.royale.core.IBead#strand
+		 *  
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.UIBase
+		 *  @royaleignorecoercion org.apache.royale.core.IBead
+		 *  @royaleignorecoercion org.apache.royale.core.IParent
+		 *  @royaleignorecoercion org.apache.royale.events.IEventDispatcher
+		 */
+		override public function set strand(value:IStrand):void
+		{
+			super.strand = value;
+            
+			// add an input field
+			input = new TextInput();
+            // input.className = "NumericStepperInput";
+            // input.typeNames = "NumericStepperInput";
+			(value as IParent).addElement(input);
+			// COMPILE::JS
+			// {
+	        //     input.positioner.style.display = 'inline-block';
+    	    //     input.positioner.style.width = '100px';
+			// }
+			// add a spinner
+			spinner = new Spinner();
+			spinner.addBead( (value as UIBase).model as IBead);
+			(value as IParent).addElement(spinner);
+			// delay this until the resize event in JS
+			COMPILE::SWF
+			{
+				spinner.height = input.height;
+				spinner.width = input.height/2;
+			}
+			// COMPILE::JS
+			// {
+	        //     spinner.positioner.style.display = 'inline-block';
+			// }
+			
+			// listen for changes to the text input field which will reset the
+			// value. ideally, we should either set the input to accept only
+			// numeric values or, barring that, reject non-numeric entries. we
+			// cannot do that right now however.
+			input.addEventListener("change",inputChangeHandler);
+			
+			// listen for change events on the spinner so the value can be updated as
+			// as resizing the component
+			spinner.addEventListener("valueChange",spinnerValueChanged);
+			IEventDispatcher(value).addEventListener("widthChanged",sizeChangeHandler);
+			IEventDispatcher(value).addEventListener("heightChanged",sizeChangeHandler);
+            IEventDispatcher(value).addEventListener("sizeChanged",sizeChangeHandler);
+			
+			// listen for changes to the model itself and update the UI accordingly
+			IEventDispatcher(UIBase(value).model).addEventListener("valueChange",modelChangeHandler);
+			IEventDispatcher(UIBase(value).model).addEventListener("minimumChange",modelChangeHandler);
+			IEventDispatcher(UIBase(value).model).addEventListener("maximumChange",modelChangeHandler);
+			IEventDispatcher(UIBase(value).model).addEventListener("stepSizeChange",modelChangeHandler);
+			IEventDispatcher(UIBase(value).model).addEventListener("snapIntervalChange",modelChangeHandler);
+			
+			input.text = String(spinner.value);
+			
+			COMPILE::SWF
+			{
+				var host:ILayoutChild = ILayoutChild(value);
+				
+				// Complete the setup if the height is sized to content or has been explicitly set
+				// and the width is sized to content or has been explicitly set
+				if ((host.isHeightSizedToContent() || !isNaN(host.explicitHeight)) &&
+					(host.isWidthSizedToContent() || !isNaN(host.explicitWidth)))
+					sizeChangeHandler(null);
+			}
+			COMPILE::JS
+			{
+				// always run size change since there are no size change events
+				sizeChangeHandler(null);
+			}
+					
+		}
+		
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.core.UIBase
+		 */
+		private function sizeChangeHandler(event:Event) : void
+		{
+			// first reads
+			var widthToContent:Boolean = (_strand as UIBase).isWidthSizedToContent();
+			var inputWidth:Number = input.width;
+			var inputHeight:Number = input.height;
+			var strandWidth:Number;
+			if (!widthToContent)
+			{
+				strandWidth = (_strand as UIBase).width;
+			}
+			// COMPILE::JS
+			// {
+			// 	spinner.height = inputHeight;
+			// 	spinner.width = inputHeight/2;
+			// }
+			
+			// input.x = 0;
+			// input.y = 0;
+			if (!widthToContent)
+				input.width = strandWidth - spinner.width - 2;
+			
+			COMPILE::SWF
+			{
+				spinner.x = inputWidth;
+				spinner.y = 0;
+			}
+		}
+		
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.events.IEventDispatcher
+		 */
+		private function spinnerValueChanged(event:ValueChangeEvent) : void
+		{
+			input.text = "" + spinner.value;
+			
+			var newEvent:ValueChangeEvent = ValueChangeEvent.createUpdateEvent(_strand, "value", event.oldValue, event.newValue);
+			IEventDispatcher(_strand).dispatchEvent(newEvent);
+		}
+		
+		/**
+		 * @private
+		 */
+		private function inputChangeHandler(event:Event) : void
+		{
+			var newValue:Number = Number(input.text);
+
+			if( !isNaN(newValue) ) {
+				spinner.value = newValue;
+			}
+			else {
+				input.text = String(spinner.value);
+			}
+		}
+		
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.core.UIBase
+		 * @royaleignorecoercion org.apache.royale.core.IRangeModel
+		 */
+		private function modelChangeHandler( event:Event ) : void
+		{
+			var n:Number = IRangeModel(UIBase(_strand).model).value;
+			input.text = String(IRangeModel(UIBase(_strand).model).value);
+		}
+		
+		/**
+		 *  The area containing the TextInput and Spinner controls.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.IParentIUIBase
+		 */
+		public function get contentView():IParentIUIBase
+		{
+			return _strand as IParentIUIBase;
+		}
+		
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.core.IUIBase
+		 */
+		public function get resizableView():IUIBase
+		{
+			return _strand as IUIBase;
+		}
+	}
+}
diff --git a/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/SpinnerView.as b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/SpinnerView.as
new file mode 100644
index 0000000..5bd0669
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/royale/org/apache/royale/jewel/beads/views/SpinnerView.as
@@ -0,0 +1,212 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+package org.apache.royale.jewel.beads.views
+{
+COMPILE::SWF {
+	import flash.display.DisplayObject;
+}
+
+    import org.apache.royale.core.BeadViewBase;
+	import org.apache.royale.core.IBeadModel;
+	import org.apache.royale.core.IBeadView;
+	import org.apache.royale.core.IRangeModel;
+	import org.apache.royale.core.IStrand;
+	import org.apache.royale.core.ILayoutChild;
+	import org.apache.royale.core.UIBase;
+	import org.apache.royale.events.Event;
+	import org.apache.royale.events.IEventDispatcher;
+	import org.apache.royale.jewel.Button;
+	// import org.apache.royale.jewel.beads.controllers.ButtonAutoRepeatController;
+    import org.apache.royale.jewel.beads.controls.spinner.ISpinnerView
+
+COMPILE::JS {
+	import org.apache.royale.jewel.beads.controllers.SpinnerMouseController;
+}
+
+	/**
+	 *  The SpinnerView class creates the visual elements of the org.apache.royale.jewel.Spinner
+	 *  component.
+	 *
+	 *  @langversion 3.0
+	 *  @playerversion Flash 10.2
+	 *  @playerversion AIR 2.6
+	 *  @productversion Royale 0.9.3
+	 */
+	public class SpinnerView extends BeadViewBase implements ISpinnerView, IBeadView
+	{
+		/**
+		 *  constructor.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		public function SpinnerView()
+		{
+		}
+
+		private var rangeModel:IRangeModel;
+
+		COMPILE::JS {
+		public var _increment:Button;
+        public var _decrement:Button;
+        private var controller:SpinnerMouseController;
+		}
+
+		COMPILE::SWF {
+		private var _decrement:DisplayObject;
+		private var _increment:DisplayObject;
+		}
+
+		/**
+		 *  @copy org.apache.royale.core.IBead#strand
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 *  @royaleignorecoercion org.apache.royale.core.UIBase
+		 *  @royaleignorecoercion org.apache.royale.events.IEventDispatcher
+		 */
+		override public function set strand(value:IStrand):void
+		{
+			super.strand = value;
+
+            COMPILE::SWF {
+				_increment = new Button();
+				// Button(_increment).addBead(new UpArrowButtonView());
+				// Button(_increment).addBead(new ButtonAutoRepeatController());
+				_decrement = new Button();
+				// Button(_decrement).addBead(new DownArrowButtonView());
+				// Button(_decrement).addBead(new ButtonAutoRepeatController());
+
+				// Button(_increment).x = 0;
+				// Button(_increment).y = 0;
+				// Button(_decrement).x = 0;
+				// Button(_decrement).y = Button(_increment).height;
+
+				UIBase(_strand).addChild(_decrement);
+				UIBase(_strand).addChild(_increment);
+				rangeModel = _strand.getBeadByType(IBeadModel) as IRangeModel;
+			}
+			
+            IEventDispatcher(value).addEventListener("widthChanged",sizeChangeHandler);
+			IEventDispatcher(value).addEventListener("heightChanged",sizeChangeHandler);
+			
+            COMPILE::JS {
+				var host:UIBase = value as UIBase;
+				// depending on the surrounding layout, the element can be offset without this.
+				//host.element.style.position = "absolute";
+
+				_increment = new Button();
+                _increment.addClass("up");
+				_increment.text = '\u25B2';
+				host.addElement(_increment);
+				// _increment.positioner.style.display = 'block';
+
+				_decrement = new Button();
+                _decrement.addClass("down");
+				_decrement.text = '\u25BC';
+				// _decrement.positioner.style.display = 'block';
+				host.addElement(_decrement);
+
+// add this in CSS!
+				// controller = new SpinnerMouseController();
+				// host.addBead(controller);
+			}
+				
+			COMPILE::SWF
+			{
+				var host:ILayoutChild = ILayoutChild(value);
+				
+				// Complete the setup if the height is sized to content or has been explicitly set
+				// and the width is sized to content or has been explicitly set
+				if ((host.isHeightSizedToContent() || !isNaN(host.explicitHeight)) &&
+					(host.isWidthSizedToContent() || !isNaN(host.explicitWidth)))
+					sizeChangeHandler(null);
+			}
+			COMPILE::JS
+			{
+				// always run size change since there are no size change events
+				sizeChangeHandler(null);
+			}
+		}
+
+		/**
+		 *  The component for decrementing the org.apache.royale.jewel.Spinner value.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		COMPILE::SWF
+		public function get decrement():DisplayObject
+		{
+			return _decrement;
+		}
+		COMPILE::JS
+		public function get decrement():Button
+		{
+			return _decrement;
+		}
+
+		/**
+		 *  The component for incrementing the org.apache.royale.jewel.Spinner value.
+		 *
+		 *  @langversion 3.0
+		 *  @playerversion Flash 10.2
+		 *  @playerversion AIR 2.6
+		 *  @productversion Royale 0.9.3
+		 */
+		COMPILE::SWF
+		public function get increment():DisplayObject
+		{
+			return _increment;
+		}
+		COMPILE::JS
+		public function get increment():Button
+		{
+			return _increment;
+		}
+
+		/**
+		 * @private
+		 * @royaleignorecoercion org.apache.royale.core.UIBase
+		 */
+		private function sizeChangeHandler( event:Event ) : void
+		{
+            var w:Number = UIBase(_strand).width;
+            var h:Number =  UIBase(_strand).height / 2;
+			// _increment.width = w;
+			// _increment.height = h;
+			COMPILE::SWF
+			{
+			_increment.y      = 0;
+			}
+			// _decrement.width = w;
+			// _decrement.height = h;
+			COMPILE::SWF
+			{
+			_decrement.y      = h;
+			}
+		}
+	}
+}
diff --git a/frameworks/projects/Jewel/src/main/sass/components/_numericstepper.sass b/frameworks/projects/Jewel/src/main/sass/components/_numericstepper.sass
new file mode 100644
index 0000000..61db15e
--- /dev/null
+++ b/frameworks/projects/Jewel/src/main/sass/components/_numericstepper.sass
@@ -0,0 +1,59 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// Jewel NumericStepper
+
+// NumericStepper variables
+
+.jewel.numericstepper    
+    .jewel.textinput
+        display: inline-flex
+    
+j|NumericStepper
+    IBeadModel: ClassReference("org.apache.royale.jewel.beads.models.RangeModel")
+    IBeadView: ClassReference("org.apache.royale.jewel.beads.views.NumericStepperView")
+
+@media -royale-swf
+    j|NumericStepper
+        // iBorderBead: ClassReference('org.apache.royale.html.beads.SingleLineBorderBead')
+        // iBackgroundBead: ClassReference('org.apache.royale.html.beads.SolidBackgroundBead')
+        // padding: 0px
+        // border-style: none
+
+
+// Jewel Spinner
+
+// Spinner variables
+.jewel.spinner
+    display: inline-flex
+    flex-direction: column
+    vertical-align: middle
+    .jewel.button
+        display: flex
+    
+j|Spinner
+    IBeadModel: ClassReference("org.apache.royale.jewel.beads.models.RangeModel")
+    IBeadView:  ClassReference("org.apache.royale.jewel.beads.views.SpinnerView")
+    IBeadController:  ClassReference("org.apache.royale.jewel.beads.controllers.SpinnerMouseController")
+    // width: 16px
+    // height: 32px
+
+@media -royale-swf
+    j|Spinner
+        // IBeadController: ClassReference("org.apache.royale.html.beads.controllers.SpinnerMouseController")
\ No newline at end of file
diff --git a/frameworks/projects/Jewel/src/main/sass/defaults.sass b/frameworks/projects/Jewel/src/main/sass/defaults.sass
index 065a751..5a53044 100644
--- a/frameworks/projects/Jewel/src/main/sass/defaults.sass
+++ b/frameworks/projects/Jewel/src/main/sass/defaults.sass
@@ -39,6 +39,7 @@
 @import "components/layout"
 @import "components/list"
 @import "components/navigation"
+@import "components/numericstepper"
 @import "components/radiobutton"
 @import "components/sectioncontent"
 @import "components/slider"
diff --git a/frameworks/themes/JewelTheme/src/main/resources/defaults.css b/frameworks/themes/JewelTheme/src/main/resources/defaults.css
index 9390150..e3833e0 100644
--- a/frameworks/themes/JewelTheme/src/main/resources/defaults.css
+++ b/frameworks/themes/JewelTheme/src/main/resources/defaults.css
@@ -453,6 +453,34 @@ j|Card {
   background-color: #ffffff;
 }
 
+.jewel.numericstepper .jewel.textinput input {
+  width: 8em;
+  border-radius: 0.25rem 0px 0px 0.25rem;
+}
+
+.jewel.spinner .jewel.button {
+  color: transparent;
+  border-bottom-left-radius: 0px;
+  border-top-left-radius: 0px;
+  border-left: 0px;
+  padding: 6px;
+  height: 20px;
+}
+.jewel.spinner .jewel.button.up {
+  border-bottom-right-radius: 0px;
+}
+.jewel.spinner .jewel.button.up::after {
+  background-size: 40%;
+  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 22 22' version='1.1' xmlns='http://www.w3.org/2000/svg'%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Crect x='0' y='0' width='22' height='22'%3E%3C/rect%3E%3Cpath d='M18.3333333,2.75 L17.4166667,2.75 L17.4166667,0.916666667 L15.5833333,0.916666667 L15.5833333,2.75 L6.41666667,2.75 L6.41666667,0.916666667 L4.58333333,0.916666667 L4.58333333,2.75 L3.66666667,2.75 C2.65833333,2.75 1.83333333,3.575 1.83333 [...]
+}
+.jewel.spinner .jewel.button.down {
+  border-top-right-radius: 0px;
+}
+.jewel.spinner .jewel.button.down::after {
+  background-size: 40%;
+  background: url("data:image/svg+xml,%3Csvg viewBox='0 0 22 22' version='1.1' xmlns='http://www.w3.org/2000/svg'%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'%3E%3Crect x='0' y='0' width='22' height='22'%3E%3C/rect%3E%3Cpath d='M18.3333333,2.75 L17.4166667,2.75 L17.4166667,0.916666667 L15.5833333,0.916666667 L15.5833333,2.75 L6.41666667,2.75 L6.41666667,0.916666667 L4.58333333,0.916666667 L4.58333333,2.75 L3.66666667,2.75 C2.65833333,2.75 1.83333333,3.575 1.83333 [...]
+}
+
 .jewel.radiobutton input + span::before {
   background: linear-gradient(white, #f3f3f3);
   border: 1px solid #b3b3b3;
diff --git a/frameworks/themes/JewelTheme/src/main/sass/components-primary/_numericstepper.sass b/frameworks/themes/JewelTheme/src/main/sass/components-primary/_numericstepper.sass
new file mode 100644
index 0000000..5384f33
--- /dev/null
+++ b/frameworks/themes/JewelTheme/src/main/sass/components-primary/_numericstepper.sass
@@ -0,0 +1,61 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  Licensed to the Apache Software Foundation (ASF) under one or more
+//  contributor license agreements.  See the NOTICE file distributed with
+//  this work for additional information regarding copyright ownership.
+//  The ASF licenses this file to You under the Apache License, Version 2.0
+//  (the "License"); you may not use this file except in compliance with
+//  the License.  You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+//  Unless required by applicable law or agreed to in writing, software
+//  distributed under the License is distributed on an "AS IS" BASIS,
+//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+//  See the License for the specific language governing permissions and
+//  limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+// Jewel NumericStepper
+
+// NumericStepper variables
+$numericstepper-input-width: 8em
+$numericstepper-border-radius: $border-radius
+
+.jewel.numericstepper
+    .jewel.textinput
+        input
+            width: $numericstepper-input-width
+            border-radius: $numericstepper-border-radius 0px 0px $numericstepper-border-radius
+
+
+
+// Jewel Spinner
+
+// Spinner variables
+.jewel.spinner
+    
+    .jewel.button
+        color: transparent
+        border-bottom-left-radius: 0px
+        border-top-left-radius: 0px
+        border-left: 0px
+
+        padding: 6px
+        height: 20px
+
+        &.up
+            border-bottom-right-radius: 0px
+
+            &::after
+                background-size: 40%
+                background: encodeSVG("<svg viewBox='0 0 22 22' version='1.1' xmlns='http://www.w3.org/2000/svg'><g stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'><rect x='0' y='0' width='22' height='22'></rect><path d='M18.3333333,2.75 L17.4166667,2.75 L17.4166667,0.916666667 L15.5833333,0.916666667 L15.5833333,2.75 L6.41666667,2.75 L6.41666667,0.916666667 L4.58333333,0.916666667 L4.58333333,2.75 L3.66666667,2.75 C2.65833333,2.75 1.83333333,3.575 1.83333333,4.58333333 L1 [...]
+
+        &.down
+            border-top-right-radius: 0px
+
+            &::after
+                background-size: 40%
+                background: encodeSVG("<svg viewBox='0 0 22 22' version='1.1' xmlns='http://www.w3.org/2000/svg'><g stroke='none' stroke-width='1' fill='none' fill-rule='evenodd'><rect x='0' y='0' width='22' height='22'></rect><path d='M18.3333333,2.75 L17.4166667,2.75 L17.4166667,0.916666667 L15.5833333,0.916666667 L15.5833333,2.75 L6.41666667,2.75 L6.41666667,0.916666667 L4.58333333,0.916666667 L4.58333333,2.75 L3.66666667,2.75 C2.65833333,2.75 1.83333333,3.575 1.83333333,4.58333333 L1 [...]
+        
\ No newline at end of file
diff --git a/frameworks/themes/JewelTheme/src/main/sass/defaults.sass b/frameworks/themes/JewelTheme/src/main/sass/defaults.sass
index de61c8a..9618266 100644
--- a/frameworks/themes/JewelTheme/src/main/sass/defaults.sass
+++ b/frameworks/themes/JewelTheme/src/main/sass/defaults.sass
@@ -41,6 +41,7 @@
 @import "components-primary/label"
 @import "components-primary/list"
 @import "components-primary/navigation"
+@import "components-primary/numericstepper"
 @import "components-primary/radiobutton"
 @import "components-primary/slider"
 @import "components-primary/table"