blob: 8a99262c8f2ce4fcae14be7ad36cdbe750be2466 [file] [log] [blame]
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:productsView="productsView.*"
layout="vertical"
currentState="browse"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
styleName="catalogPanel">
<mx:Metadata>
[Event(name="purchase", type="samples.flexstore.ProductThumbEvent")]
[Event(name="compare", type="samples.flexstore.ProductThumbEvent")]
[Event(name="details", type="samples.flexstore.ProductThumbEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import flash.utils.Dictionary;
import mx.collections.ArrayCollection;
import mx.collections.IViewCursor;
import mx.core.DragSource;
import mx.core.IUIComponent;
import mx.effects.Effect;
import mx.effects.Fade;
import mx.effects.Move;
import mx.events.EffectEvent;
import mx.events.DragEvent;
import mx.managers.DragManager;
import samples.flexstore.Product;
import samples.flexstore.ProductFilter;
import samples.flexstore.ProductFilterEvent;
import samples.flexstore.ProductThumbEvent;
private var accepted:Dictionary = new Dictionary();
private var thumbnails:Array;
private var filterCount:int;
private var thumbnailState:String = 'browse'; //either 'browse' or 'compare'
[Bindable]
private var titleButtons:CatalogTitleButtons; //the buttons that also allow the panel to switch sides
override protected function createChildren():void
{
super.createChildren();
titleButtons = new CatalogTitleButtons();
titleBar.addChild(titleButtons);
}
override protected function layoutChrome(unscaledWidth:Number, unscaledHeight:Number):void
{
super.layoutChrome(unscaledWidth, unscaledHeight);
//when adding to a UIComponent (not a Container) need explicit width/height
titleButtons.width = unscaledWidth / 2; //make it big so as we add cart items we can stretch)
titleButtons.height = titleButtons.measuredHeight;
//this placement algorithm is pretty hacky, there are better ways that probably
//involve copying more of the Panel layoutChrome method and supporting methods
titleButtons.move(statusTextField.x - titleButtons.width, titleTextField.y);
}
[Bindable]
public var cartCount:int;
//-----------------------------
// catalog
//-----------------------------
private var _catalog:ArrayCollection;
[Bindable]
public function set catalog(c:ArrayCollection):void
{
_catalog = c;
createThumbnails();
}
public function get catalog():ArrayCollection
{
return _catalog;
}
//----------------------------------------------------------------------
// methods
//----------------------------------------------------------------------
private function createThumbnails():void
{
var i:int; //variables are hoisted up in scope so declare here to avoid warning
if (thumbnails != null)
{
for (i=0; i < thumbnails.length; i++)
{
thumbContent.removeChild(thumbnails[i]);
}
}
var row:int = 0;
var col:int = -1;
var n:int = catalog.length;
thumbnails = new Array(n);
filterCount = n;
for (i=0; i < n; i++)
{
var thumb:ProductCatalogThumbnail = new ProductCatalogThumbnail();
thumbnails[i] = thumb;
thumbnails[i].showInAutomationHierarchy = true;
thumb.product = catalog.getItemAt(i) as Product;
accepted[thumb.product] = true;
thumbContent.addChild(thumb);
thumb.addEventListener(ProductThumbEvent.PURCHASE, productThumbEventHandler);
thumb.addEventListener(ProductThumbEvent.COMPARE, productThumbEventHandler);
thumb.addEventListener(ProductThumbEvent.DETAILS, productThumbEventHandler);
thumb.addEventListener(DragEvent.DRAG_START,thumbDragStartHandler);
}
layoutCatalog();
}
private function thumbDragStartHandler(event:MouseEvent):void
{
if (DragManager.isDragging == false)
{
var thumb:ProductCatalogThumbnail = event.target as ProductCatalogThumbnail;
var ds:DragSource = new DragSource();
ds.addData(thumb.product, "product");
var di:ProductCatalogThumbnail = new ProductCatalogThumbnail();
di.product = thumb.product;
//the offset logic is honestly not the most intuitive but
//it's necessary to get the dragProxy positioned correctly
DragManager.doDrag(thumbContent, ds, event, di, -thumb.x,
-thumb.y + thumbContent.verticalScrollPosition,
0.5, false);
}
}
public function filter(productFilter:ProductFilter, live:Boolean):void
{
currentState = "browse";
thumbnailState = "browse";
var count:int=0;
var n:int = thumbnails.length;
var fadeOut:Fade = new Fade();
fadeOut.alphaFrom = 1;
fadeOut.alphaTo = .1;
var targets:Array = [];
for (var i:int = 0; i < n; i++)
{
var thumb:ProductCatalogThumbnail = thumbnails[i];
var product:Product = thumb.product;
if (productFilter.accept(product))
{
accepted[product] = true;
thumb.alpha = 1;
count++;
}
else
{
accepted[product] = false;
if (live)
{
thumb.alpha = .1;
}
else if (thumb.alpha == 1) //only fade if we hadn't started
{
targets.push(thumb);
}
}
}
productFilter.count = count;
filterCount = count;
if (targets.length > 0)
{
fadeOut.targets = targets;
fadeOut.play();
fadeOut.addEventListener(EffectEvent.EFFECT_END,
function(event:EffectEvent):void
{
layoutCatalog();
});
}
else if (!live)
{
layoutCatalog();
}
}
private function layoutCatalog():Effect
{
var tileWidth:Number;
var tileHeight:Number;
var numCols:int;
if (filterCount > 9 || currentState == "compare")
{
numCols = 4;
tileWidth = ProductCatalogThumbnail.COL_WIDTH_4;
tileWidth = currentState == "compare"
? ProductCatalogThumbnail.COMPARE_WIDTH
: ProductCatalogThumbnail.COL_WIDTH_4
tileHeight = currentState == "compare"
? height - 4
: ProductCatalogThumbnail.COL_HEIGHT_4;
}
else if (filterCount > 4)
{
numCols = 3;
tileWidth = ProductCatalogThumbnail.COL_WIDTH_3;
tileHeight = ProductCatalogThumbnail.COL_HEIGHT_3;
}
else if (filterCount <= 9)
{
numCols = 2;
tileWidth = ProductCatalogThumbnail.COL_WIDTH_2;
tileHeight = ProductCatalogThumbnail.COL_HEIGHT_2;
}
else
{
}
var row:int = 0;
var col:int = -1;
var move:Move = null;
var n:int = catalog.length;
for (var i:int = 0 ; i < n ; i++)
{
var product:Product = catalog.getItemAt(i) as Product;
var thumb:ProductCatalogThumbnail = thumbnails[i];
if (accepted[product])
{
thumb.currentState = "" + numCols + "cols";
col++;
if (col > numCols - 1)
{
row++;
col = 0;
}
var xTo:Number = col * (tileWidth + ProductCatalogThumbnail.HORIZONTAL_GAP);
var yTo:Number = row * (tileHeight + ProductCatalogThumbnail.VERTICAL_GAP);
// If the thumbnail is already visible
// animate it to its new position.
if (thumb.visible)
{
// Animate only if the position is different
// from its current position.
if (thumb.x != xTo || thumb.y != yTo)
{
move = new Move(thumb);
move.xTo = xTo;
move.yTo = yTo;
move.play();
}
}
// If the thumbnail was not previously visible, sets its
// x and y coordinates. We'll make it reappear after all
// the visible thumbnails have reached their new position.
else
{
thumb.x = xTo;
thumb.y = yTo;
thumb.includeInLayout = true;
}
}
else
{
thumb.visible = false;
thumb.includeInLayout = false;
}
}
if (!move)
{
// No visible thumbnails were animated to a new position;
// fade in newly selected thumbnails right away.
fadeInThumbnails();
}
else
{
//since movement is happening get the scrollbar back to the top
thumbContent.verticalScrollPosition = 0;
// Fade in newly selected thumbnails after the last
// visible thumbnail has moved to its new position.
move.addEventListener(EffectEvent.EFFECT_END,
function(event:EffectEvent):void
{
fadeInThumbnails();
});
}
//return the last move to watch
return move;
}
//return the last effect so we could add effectEnd handler if desired
private function fadeInThumbnails():void
{
var n:int = thumbnails.length;
var effect:Fade = new Fade();
effect.alphaTo = 1;
var targets:Array = [];
for (var i:int = 0; i < n ; i++)
{
var thumb:ProductCatalogThumbnail = thumbnails[i];
if (accepted[thumb.product] && !thumb.visible)
{
thumb.alpha = 0;
thumb.visible = true;
targets.push(thumb);
}
}
if (targets.length > 0)
{
effect.targets = targets;
effect.play();
}
}
private function showDetails(product:Product):void
{
if (currentState == "details")
{
details.product = product;
return;
}
var row:int = -1;
//should be computed using border metrics instead of hard-coding the 20, but...
var xTo:Number = thumbContent.width - ProductCatalogThumbnail.COL_WIDTH_4 - 20;
var yTo:Number;
var move:Move;
var first:Boolean = true;
var selectedThumb:ProductCatalogThumbnail;
var n:int = thumbnails.length;
for (var i:int = 0; i < n; i++)
{
var thumb:ProductCatalogThumbnail = thumbnails[i];
if (thumb.visible)
{
row++;
yTo = row * (ProductCatalogThumbnail.COL_HEIGHT_4 + ProductCatalogThumbnail.VERTICAL_GAP);
thumb.currentState = "4cols";
if (thumb.x != xTo || thumb.y != yTo)
{
move = new Move(thumb);
if (first)
{
move.addEventListener(EffectEvent.EFFECT_END,
function(event:EffectEvent):void
{
details.product = product;
currentState = "details";
});
first = false;
}
move.xTo = xTo;
move.yTo = yTo;
move.play();
}
if (thumb.product == product)
{
selectedThumb = thumb;
}
}
}
if (selectedThumb != null)
{
//make sure that the selected thumb is visible in the list on the right
move.addEventListener(EffectEvent.EFFECT_END,
function(e:EffectEvent):void
{
var curpos:int = thumbContent.verticalScrollPosition;
if (selectedThumb.y < curpos)
{
thumbContent.verticalScrollPosition = y;
}
else if (selectedThumb.y + ProductCatalogThumbnail.COL_HEIGHT_4 > curpos + thumbContent.height)
{
//this logic doesn't seem to be perfect but it will do
var diff:int = selectedThumb.y - (curpos + thumbContent.height)
thumbContent.verticalScrollPosition += diff + ProductCatalogThumbnail.COL_HEIGHT_4 + ProductCatalogThumbnail.VERTICAL_GAP;
}
});
}
}
private function productThumbEventHandler(event:ProductThumbEvent):void
{
if (event.type == ProductThumbEvent.DETAILS)
{
showDetails(event.product);
}
else if (event.type == ProductThumbEvent.BROWSE)
{
if (thumbnailState == "browse")
{
currentState = "browse";
layoutCatalog();
}
else
{
compare();
}
}
else
{
dispatchEvent(event);
}
}
public function compare(toCompare:Array=null):void
{
currentState = "compare";
thumbnailState = "compare";
if (toCompare != null)
{
var n:int = thumbnails.length;
for (var i:int = 0; i < n; i++)
{
accepted[thumbnails[i].product] = false;
}
for (i=0; i < toCompare.length; i++)
{
accepted[toCompare[i]] = true;
}
}
var lastEffect:Effect = layoutCatalog();
if (lastEffect != null)
{
lastEffect.addEventListener(EffectEvent.EFFECT_END,
function (event:EffectEvent):void
{
setCompareState();
});
}
else
{
setCompareState();
}
}
private function setCompareState():void
{
//avoid an issue if the user clicks quickly where we move into
//compare state even though we're no longer in compare
if (currentState == "compare")
{
var n:int = thumbnails.length;
for (var i:int = 0; i < n; i++)
{
var thumb:ProductCatalogThumbnail = thumbnails[i];
if (accepted[thumb.product])
{
thumb.currentState = "compare";
}
}
}
}
]]>
</mx:Script>
<mx:Binding source="cartCount" destination="titleButtons.cartCount" />
<!-- two-way binding between the states of panel title buttons and the product view state -->
<mx:Binding source="ProductsView(parentDocument).currentState" destination="titleButtons.currentState" />
<mx:Binding destination="ProductsView(parentDocument).currentState" source="titleButtons.currentState" />
<mx:Canvas width="100%" height="100%"
verticalScrollPolicy="off"
horizontalScrollPolicy="off"
paddingRight="0">
<mx:Canvas id="thumbContent" width="100%" height="100%"
horizontalScrollPolicy="off"/>
<productsView:ProductDetails id="details"
width="{ProductCatalogThumbnail.COL_WIDTH_4 * 3}"
height="100%"
visible="false"
compare="productThumbEventHandler(event)"
purchase="productThumbEventHandler(event)"
browse="productThumbEventHandler(event)" />
</mx:Canvas>
<mx:states>
<mx:State name="browse">
<mx:SetProperty name="title" value="Browse"/>
</mx:State>
<mx:State name="compare">
<mx:SetProperty name="title" value="Product Comparison"/>
</mx:State>
<mx:State name="details">
<mx:SetProperty name="title" value="Product Details"/>
<mx:SetProperty target="{details}" name="visible" value="true"/>
</mx:State>
</mx:states>
</mx:Panel>