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

yishayw 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 e9ceb39f15 Added dropdown
     new 445f28e322 Merge branch 'develop' of 
https://github.com/apache/royale-asjs into develop
e9ceb39f15 is described below

commit e9ceb39f15a3ef03b509ee51d28e9529004aa452
Author: Yishay Weiss <[email protected]>
AuthorDate: Mon Apr 13 19:20:09 2026 +0300

    Added dropdown
---
 .../projects/Style/src/main/resources/defaults.css |   4 +
 .../Style/src/main/resources/style-manifest.xml    |   2 +
 .../royale/org/apache/royale/style/Dropdown.as     | 183 +++++++++++++++++++
 .../org/apache/royale/style/skins/DropdownSkin.as  | 200 +++++++++++++++++++++
 .../org/apache/royale/style/skins/IDropdownSkin.as |  29 +++
 .../style/stylebeads/states/FocusWithinState.as    |  34 ++++
 6 files changed, 452 insertions(+)

diff --git a/frameworks/projects/Style/src/main/resources/defaults.css 
b/frameworks/projects/Style/src/main/resources/defaults.css
index 17ae0a005d..2e1c89a254 100644
--- a/frameworks/projects/Style/src/main/resources/defaults.css
+++ b/frameworks/projects/Style/src/main/resources/defaults.css
@@ -51,6 +51,10 @@ CheckBox
 {
        IStyleSkin: 
ClassReference("org.apache.royale.style.skins.CheckBoxSkin");
 }
+Dropdown
+{
+       IStyleSkin: 
ClassReference("org.apache.royale.style.skins.DropdownSkin");
+}
 
 DataItemRenderer
 {
diff --git a/frameworks/projects/Style/src/main/resources/style-manifest.xml 
b/frameworks/projects/Style/src/main/resources/style-manifest.xml
index 1dd68b067c..f881df56f1 100644
--- a/frameworks/projects/Style/src/main/resources/style-manifest.xml
+++ b/frameworks/projects/Style/src/main/resources/style-manifest.xml
@@ -28,6 +28,7 @@
   <component id="Container" class="org.apache.royale.style.Container"/>
   <component id="CheckBox" class="org.apache.royale.style.CheckBox"/>
   <component id="Divider" class="org.apache.royale.style.Divider"/>
+  <component id="Dropdown" class="org.apache.royale.style.Dropdown"/>
   <component id="FlexContainer" class="org.apache.royale.style.FlexContainer"/>
   <component id="GridContainer" class="org.apache.royale.style.GridContainer"/>
   <component id="StyleSkin" class="org.apache.royale.style.StyleSkin"/>
@@ -49,6 +50,7 @@
   <component id="DataState" 
class="org.apache.royale.style.stylebeads.states.attribute.DataState"/>
   <component id="AriaState" 
class="org.apache.royale.style.stylebeads.states.attribute.AriaState"/>
   <component id="InertState" 
class="org.apache.royale.style.stylebeads.states.attribute.InertState"/>
+  <component id="FocusWithinState" 
class="org.apache.royale.style.stylebeads.states.FocusWithinState"/>
   <component id="OpenState" 
class="org.apache.royale.style.stylebeads.states.attribute.OpenState"/>
   <component id="RTLState" 
class="org.apache.royale.style.stylebeads.states.attribute.RTLState"/>
   <component id="SelectedState" 
class="org.apache.royale.style.stylebeads.states.attribute.SelectedState"/>
diff --git 
a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/Dropdown.as 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/Dropdown.as
new file mode 100644
index 0000000000..77834d14df
--- /dev/null
+++ 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/Dropdown.as
@@ -0,0 +1,183 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.style
+{
+       COMPILE::JS
+       {
+               import org.apache.royale.core.WrappedHTMLElement;
+       }
+       import org.apache.royale.core.IChild;
+       import org.apache.royale.events.Event;
+       import org.apache.royale.style.elements.Div;
+       import org.apache.royale.style.skins.IDropdownSkin;
+       import org.apache.royale.style.StyleUIBase;
+
+       [Event(name="openChanged", type="org.apache.royale.events.Event")]
+
+       public class Dropdown extends Group
+       {
+               public function Dropdown()
+               {
+                       super();
+                       useWrapperStyle = true;
+               }
+
+               protected var triggerElem:Div;
+               protected var contentElem:Div;
+
+               private var _triggerText:String;
+
+               public function get triggerText():String
+               {
+                       return _triggerText;
+               }
+
+               public function set triggerText(value:String):void
+               {
+                       _triggerText = value;
+                       if (triggerElem)
+                               triggerElem.text = value;
+               }
+
+               private var _open:Boolean = false;
+
+               public function get open():Boolean
+               {
+                       return _open;
+               }
+
+               public function set open(value:Boolean):void
+               {
+                       if (value != _open)
+                       {
+                               _open = value;
+                               toggleAttribute("open", value);
+                               dispatchEvent(new Event("openChanged"));
+                       }
+               }
+
+               private var _disabled:Boolean = false;
+
+               public function get disabled():Boolean
+               {
+                       return _disabled;
+               }
+
+               public function set disabled(value:Boolean):void
+               {
+                       if (value != _disabled)
+                       {
+                               _disabled = value;
+                               toggleAttribute("data-disabled", value);
+                               if (triggerElem)
+                               {
+                                       COMPILE::JS
+                                       {
+                                               (triggerElem.element as 
HTMLElement).tabIndex = value ? -1 : 0;
+                                       }
+                               }
+                       }
+               }
+
+               /**
+                * Override 
numElements/addElementAt/getElementAt/getElementIndex
+                * so that MXML content goes into the content div, not the 
wrapper.
+                */
+               override public function get numElements():int
+               {
+                       return contentElem ? contentElem.numElements : 0;
+               }
+
+               override public function addElement(c:IChild, 
dispatchEvent:Boolean = true):void
+               {
+                       contentElem.addElement(c, dispatchEvent);
+                       applyItemStyles(c);
+               }
+
+               override public function addElementAt(c:IChild, index:int, 
dispatchEvent:Boolean = true):void
+               {
+                       contentElem.addElementAt(c, index, dispatchEvent);
+                       applyItemStyles(c);
+               }
+
+               override public function getElementAt(index:int):IChild
+               {
+                       return contentElem.getElementAt(index);
+               }
+
+               override public function getElementIndex(c:IChild):int
+               {
+                       return contentElem.getElementIndex(c);
+               }
+
+               COMPILE::JS
+               override protected function createElement():WrappedHTMLElement
+               {
+                       var elem:WrappedHTMLElement = super.createElement();
+
+                       triggerElem = new Div();
+                       triggerElem.tabIndex = _disabled ? -1 : 0;
+                       super.addElement(triggerElem);
+                       if (_triggerText)
+                               triggerElem.text = _triggerText;
+
+                       contentElem = new Div();
+                       super.addElement(contentElem);
+
+                       if (_open)
+                               toggleAttribute("open", true);
+                       if (_disabled)
+                               toggleAttribute("data-disabled", true);
+
+                       element.addEventListener("focusout", handleFocusOut);
+
+                       return elem;
+               }
+
+               COMPILE::JS
+               private function handleFocusOut(event:Object):void
+               {
+                       // Close when focus leaves the dropdown entirely
+                       if (_open && !element.contains(event.relatedTarget))
+                       {
+                               open = false;
+                       }
+               }
+
+               /**
+                * @royaleignorecoercion 
org.apache.royale.style.skins.IDropdownSkin
+                */
+               override protected function applySkin():void
+               {
+                       var dropSkin:IDropdownSkin = skin as IDropdownSkin;
+                       triggerElem.setStyles(dropSkin.triggerStyles, true);
+                       contentElem.setStyles(dropSkin.contentStyles, true);
+                       // Apply item styles to any children already added
+                       for (var i:int = 0; i < numElements; i++)
+                       {
+                               applyItemStyles(getElementAt(i));
+                       }
+               }
+
+               private function applyItemStyles(c:IChild):void
+               {
+                       (c as StyleUIBase).setStyles((skin as 
IDropdownSkin).itemStyles, true);
+               }
+       }
+}
diff --git 
a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/skins/DropdownSkin.as
 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/skins/DropdownSkin.as
new file mode 100644
index 0000000000..a5069aede8
--- /dev/null
+++ 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/skins/DropdownSkin.as
@@ -0,0 +1,200 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.style.skins
+{
+       import org.apache.royale.style.StyleSkin;
+       import org.apache.royale.style.Dropdown;
+       import org.apache.royale.core.IStrand;
+       import org.apache.royale.style.colors.ThemeColorSet;
+       import org.apache.royale.style.colors.ColorSwatch;
+       import org.apache.royale.style.util.ThemeManager;
+       import org.apache.royale.style.stylebeads.layout.Display;
+       import org.apache.royale.style.stylebeads.layout.Position;
+       import org.apache.royale.style.stylebeads.layout.Top;
+       import org.apache.royale.style.stylebeads.layout.Visibility;
+       import org.apache.royale.style.stylebeads.layout.ZIndex;
+       import org.apache.royale.style.stylebeads.effects.OpacityStyle;
+       import org.apache.royale.style.stylebeads.interact.Cursor;
+       import org.apache.royale.style.stylebeads.anim.Transition;
+       import org.apache.royale.style.stylebeads.background.BackgroundColor;
+       import org.apache.royale.style.stylebeads.border.BorderRadius;
+       import org.apache.royale.style.stylebeads.border.Border;
+       import org.apache.royale.style.stylebeads.border.BorderColor;
+       import org.apache.royale.style.stylebeads.border.BorderWidth;
+       import org.apache.royale.style.stylebeads.sizing.MinWidth;
+       import org.apache.royale.style.stylebeads.spacing.Margin;
+       import org.apache.royale.style.stylebeads.spacing.Padding;
+       import org.apache.royale.style.stylebeads.states.DisabledState;
+       import org.apache.royale.style.stylebeads.states.FocusWithinState;
+       import org.apache.royale.style.stylebeads.states.HoverState;
+       import org.apache.royale.style.stylebeads.states.attribute.OpenState;
+       import org.apache.royale.style.stylebeads.states.attribute.DataState;
+       import org.apache.royale.style.stylebeads.states.GroupPseudo;
+
+       public class DropdownSkin extends StyleSkin implements IDropdownSkin
+       {
+               public function DropdownSkin()
+               {
+                       super();
+               }
+
+               /**
+                * @royaleignorecoercion org.apache.royale.style.Dropdown
+                */
+               private function get host():Dropdown
+               {
+                       return _strand as Dropdown;
+               }
+
+               override public function set strand(value:IStrand):void
+               {
+                       super.strand = value;
+                       _styles = [
+                               new Position("relative"),
+                               new Display("inline-block")
+                       ];
+                       host.setStyles(_styles);
+               }
+
+               private var _triggerStyles:Array;
+
+               public function get triggerStyles():Array
+               {
+                       if (!_triggerStyles)
+                               createTriggerStyles();
+                       return _triggerStyles;
+               }
+
+               public function set triggerStyles(value:Array):void
+               {
+                       _triggerStyles = value;
+               }
+
+               private function createTriggerStyles():void
+               {
+                       _triggerStyles = [
+                               new Cursor("pointer"),
+                               // When the wrapper has [data-disabled], 
override the trigger cursor to default
+                               new GroupPseudo([
+                                       new DataState("disabled", [
+                                               new Cursor("default")
+                                       ])
+                               ], host.getWrapperStyle())
+                       ];
+               }
+
+               private var _contentStyles:Array;
+
+               public function get contentStyles():Array
+               {
+                       if (!_contentStyles)
+                               createContentStyles();
+                       return _contentStyles;
+               }
+
+               public function set contentStyles(value:Array):void
+               {
+                       _contentStyles = value;
+               }
+
+               private function createContentStyles():void
+               {
+                       var colorSet:ThemeColorSet = 
ThemeManager.instance.activeTheme.themeColorSet;
+                       var bgColor:ColorSwatch = 
colorSet.getSwatch(ThemeColorSet.BASE, 100);
+                       var borderColor:ColorSwatch = 
colorSet.getSwatch(ThemeColorSet.NEUTRAL, 200);
+
+                       var top:Top = new Top();
+                       top.value = "100%";
+
+                       var margin:Margin = new Margin();
+                       margin.top = 1;
+
+                       var padding:Padding = new Padding();
+                       padding.block = 2;
+                       padding.inline = 0;
+
+                       _contentStyles = [
+                               new Position("absolute"),
+                               top,
+                               new ZIndex(50),
+                               new Visibility("hidden"),
+                               new OpacityStyle(0),
+                               new Transition(),
+                               new MinWidth("12rem"),
+                               new BackgroundColor(bgColor),
+                               new BorderWidth(0.25),
+                               new BorderColor(borderColor),
+                               new 
BorderRadius(ThemeManager.instance.activeTheme.radiusMD),
+                               margin,
+                               padding,
+                               new GroupPseudo([
+                                       new FocusWithinState([
+                                               new Visibility("visible"),
+                                               new OpacityStyle(100)
+                                       ])
+                               ], host.getWrapperStyle()),
+                               new GroupPseudo([
+                                       new OpenState([
+                                               new Visibility("visible"),
+                                               new OpacityStyle(100)
+                                       ])
+                               ], host.getWrapperStyle())
+                       ];
+               }
+
+               private var _itemStyles:Array;
+
+               public function get itemStyles():Array
+               {
+                       if (!_itemStyles)
+                               createItemStyles();
+                       return _itemStyles;
+               }
+
+               public function set itemStyles(value:Array):void
+               {
+                       _itemStyles = value;
+               }
+
+               private function createItemStyles():void
+               {
+                       var colorSet:ThemeColorSet = 
ThemeManager.instance.activeTheme.themeColorSet;
+                       var hoverBg:ColorSwatch = 
colorSet.getSwatch(ThemeColorSet.BASE, 200);
+
+                       var padding:Padding = new Padding();
+                       padding.block = 2;
+                       padding.inline = 3;
+
+                       _itemStyles = [
+                               new Cursor("pointer"),
+                               padding,
+                               new HoverState([
+                                       new BackgroundColor(hoverBg)
+                               ])
+                       ];
+               }
+
+               override public function update():void
+               {
+                       _triggerStyles = null;
+                       _contentStyles = null;
+                       _itemStyles = null;
+               }
+       }
+}
diff --git 
a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/skins/IDropdownSkin.as
 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/skins/IDropdownSkin.as
new file mode 100644
index 0000000000..e030a44520
--- /dev/null
+++ 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/skins/IDropdownSkin.as
@@ -0,0 +1,29 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.style.skins
+{
+       import org.apache.royale.style.IStyleSkin;
+
+       public interface IDropdownSkin extends IStyleSkin
+       {
+               function get triggerStyles():Array;
+               function get contentStyles():Array;
+               function get itemStyles():Array;
+       }
+}
diff --git 
a/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/states/FocusWithinState.as
 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/states/FocusWithinState.as
new file mode 100644
index 0000000000..2c6bc64610
--- /dev/null
+++ 
b/frameworks/projects/Style/src/main/royale/org/apache/royale/style/stylebeads/states/FocusWithinState.as
@@ -0,0 +1,34 @@
+////////////////////////////////////////////////////////////////////////////////
+//
+//  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.style.stylebeads.states
+{
+       /**
+        *  The FocusWithinState class is a style decorator that applies styles 
when
+        *  any child of the element has focus (:focus-within pseudo-class).
+        */
+       public class FocusWithinState extends LeafDecorator
+       {
+               public function FocusWithinState(styles:Array = null)
+               {
+                       super(styles);
+                       selectorDecorator = "focus-within:";
+                       ruleDecorator = ":focus-within";
+               }
+       }
+}

Reply via email to