I have JSON file with events and i want displat monthly event calendar but i can't do it. Android SDK 1.7.5 Titanium Appcelerator
I Google a lot for showing Month View / Calendar View in my Titanium application but unable to find it out which work on IPhone and Android (Almost all the screens) Both.
After that I tried to implement it by my own and got the good implementation.
To achieve this, Create project which support Android & IPhone.
Open app.js and replace the following code with the existing code and after that Press Command + Shift + F to format the code.
// Taking Screen Width
var screenWidth = 322;
var needToChangeSize = false;
var screenWidthActual = Ti.Platform.displayCaps.platformWidth;
if (Ti.Platform.osname === 'android') {
if (screenWidthActual >= 641) {
screenWidth = screenWidthActual;
needToChangeSize = true;
}
}
// Main Window of the Month View.
var win = Ti.UI.createWindow({
backgroundColor : '#FF000000',
navBarHidden : true
});
// Button at the buttom side
var backButton = Ti.UI.createButton({
bottom : '20dp',
height : '40dp',
width : '200dp'
});
// Previous Button - Tool Bar
var prevMonth = Ti.UI.createButton({
left : '15dp',
width : 'auto',
height : 'auto',
title : '<'
});
// Next Button - Tool Bar
var nextMonth = Ti.UI.createButton({
right : '15dp',
width : 'auto',
height : 'auto',
title : '>'
});
// Month Title - Tool Bar
var monthTitle = Ti.UI.createLabel({
width : '200dp',
height : '24dp',
textAlign : 'center',
color : '#3a4756',
font : {
fontSize : 20,
fontWeight : 'bold'
}
});
// Tool Bar
var toolBar = Ti.UI.createView({
top : '0dp',
width : '322dp',
height : '50dp',
backgroundColor : '#FFFFD800',
layout : 'vertical'
});
// Tool Bar - View which contain Title Prev. & Next Button
var toolBarTitle = Ti.UI.createView({
top : '3dp',
width : '322dp',
height : '24dp'
});
toolBarTitle.add(prevMonth);
toolBarTitle.add(monthTitle);
toolBarTitle.add(nextMonth);
// Tool Bar - Day's
var toolBarDays = Ti.UI.createView({
top : '2dp',
width : '322dp',
height : '22dp',
layout : 'horizontal',
left : '-1dp'
});
toolBarDays.sunday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Sun',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.monday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Mon',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.tuesday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Tue',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.wednesday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Wed',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.thursday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Thu',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.friday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Fri',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.saturday = Ti.UI.createLabel({
left : '0dp',
height : '20dp',
text : 'Sat',
width : '46dp',
textAlign : 'center',
font : {
fontSize : 12,
fontWeight : 'bold'
},
color : '#3a4756'
});
toolBarDays.add(toolBarDays.sunday);
toolBarDays.add(toolBarDays.monday);
toolBarDays.add(toolBarDays.tuesday);
toolBarDays.add(toolBarDays.wednesday);
toolBarDays.add(toolBarDays.thursday);
toolBarDays.add(toolBarDays.friday);
toolBarDays.add(toolBarDays.saturday);
// Adding Tool Bar Title View & Tool Bar Days View
toolBar.add(toolBarTitle);
toolBar.add(toolBarDays);
// Function which create day view template
dayView = function(e) {
var label = Ti.UI.createLabel({
current : e.current,
width : '46dp',
height : '44dp',
backgroundColor : '#FFDCDCDF',
text : e.day,
textAlign : 'center',
color : e.color,
font : {
fontSize : 20,
fontWeight : 'bold'
}
});
return label;
};
monthName = function(e) {
switch(e) {
case 0:
e = 'January';
break;
case 1:
e = 'February';
break;
case 2:
e = 'March';
break;
case 3:
e = 'April';
break;
case 4:
e = 'May';
break;
case 5:
e = 'June';
break;
case 6:
e = 'July';
break;
case 7:
e = 'August';
break;
case 8:
e = 'September';
break;
case 9:
e = 'October';
break;
case 10:
e = 'November';
break;
case 11:
e = 'December';
break;
};
return e;
};
// Calendar Main Function
var calView = function(a, b, c) {
var nameOfMonth = monthName(b);
//create main calendar view
var mainView = Ti.UI.createView({
layout : 'horizontal',
width : '322dp',
height : 'auto',
top : '50dp'
});
//set the time
var daysInMonth = 32 - new Date(a, b, 32).getDate();
var dayOfMonth = new Date(a, b, c).getDate();
var dayOfWeek = new Date(a, b, 1).getDay();
var daysInLastMonth = 32 - new Date(a, b - 1, 32).getDate();
var daysInNextMonth = (new Date(a, b, daysInMonth).getDay()) - 6;
//set initial day number
var dayNumber = daysInLastMonth - dayOfWeek + 1;
//get last month's days
for ( i = 0; i < dayOfWeek; i++) {
mainView.add(new dayView({
day : dayNumber,
color : '#8e959f',
current : 'no',
dayOfMonth : ''
}));
dayNumber++;
};
// reset day number for current month
dayNumber = 1;
//get this month's days
for ( i = 0; i < daysInMonth; i++) {
var newDay = new dayView({
day : dayNumber,
color : '#3a4756',
current : 'yes',
dayOfMonth : dayOfMonth
});
mainView.add(newDay);
if (newDay.text == dayOfMonth) {
newDay.color = 'white';
// newDay.backgroundImage='../libraries/calendar/pngs/monthdaytiletoday_selected.png';
newDay.backgroundColor = '#FFFFF000';
var oldDay = newDay;
}
dayNumber++;
};
dayNumber = 1;
//get remaining month's days
for ( i = 0; i > daysInNextMonth; i--) {
mainView.add(new dayView({
day : dayNumber,
color : '#8e959f',
current : 'no',
dayOfMonth : ''
}));
dayNumber++;
};
// this is the new "clicker" function, although it doesn't have a name anymore, it just is.
mainView.addEventListener('click', function(e) {
if (e.source.current == 'yes') {
// reset last day selected
if (oldDay.text == dayOfMonth) {
oldDay.color = 'white';
// oldDay.backgroundImage='../libraries/calendar/pngs/monthdaytiletoday.png';
oldDay.backgroundColor = '#FFFFF000';
} else {
oldDay.color = '#3a4756';
// oldDay.backgroundImage='../libraries/calendar/pngs/monthdaytile-Decoded.png';
oldDay.backgroundColor = '#FFDCDCDF'
}
oldDay.backgroundPaddingLeft = '0dp';
oldDay.backgroundPaddingBottom = '0dp';
// set window title with day selected, for testing purposes only
backButton.title = nameOfMonth + ' ' + e.source.text + ', ' + a;
// set characteristic of the day selected
if (e.source.text == dayOfMonth) {
// e.source.backgroundImage='../libraries/calendar/pngs/monthdaytiletoday_selected.png';
e.source.backgroundColor = '#FFFF00FF';
} else {
// e.source.backgroundImage='../libraries/calendar/pngs/monthdaytile_selected.png';
e.source.backgroundColor = '#FFFF0000';
}
e.source.backgroundPaddingLeft = '1dp';
e.source.backgroundPaddingBottom = '1dp';
e.source.color = 'white';
//this day becomes old :(
oldDay = e.source;
}
});
return mainView;
};
// what's today's date?
var setDate = new Date();
a = setDate.getFullYear();
b = setDate.getMonth();
c = setDate.getDate();
// add the three calendar views to the window for changing calendars with animation later
var prevCalendarView = null;
if (b == 0) {
prevCalendarView = calView(a - 1, 11, c);
} else {
prevCalendarView = calView(a, b - 1, c);
}
prevCalendarView.left = (screenWidth * -1) + 'dp';
var nextCalendarView = null;
if (b == 0) {
nextCalendarView = calView(a + 1, 0, c);
} else {
nextCalendarView = calView(a, b + 1, c);
}
nextCalendarView.left = screenWidth + 'dp';
var thisCalendarView = calView(a, b, c);
if (needToChangeSize == false) {
thisCalendarView.left = '-1dp';
}
monthTitle.text = monthName(b) + ' ' + a;
backButton.title = monthName(b) + ' ' + c + ', ' + a;
// add everything to the window
win.add(toolBar);
win.add(thisCalendarView);
win.add(nextCalendarView);
win.add(prevCalendarView);
win.add(backButton);
// yeah, open the window, why not?
win.open({
modal : true
});
var slideNext = Titanium.UI.createAnimation({
// left : '-322',
duration : 500
});
slideNext.left = (screenWidth * -1);
var slideReset = Titanium.UI.createAnimation({
// left : '-1',
duration : 500
});
if (needToChangeSize == false) {
slideReset.left = '-1';
} else {
slideReset.left = ((screenWidth - 644) / 2);
}
var slidePrev = Titanium.UI.createAnimation({
// left : '322',
duration : 500
});
slidePrev.left = screenWidth;
// Next Month Click Event
nextMonth.addEventListener('click', function() {
if (b == 11) {
b = 0;
a++;
} else {
b++;
}
thisCalendarView.animate(slideNext);
nextCalendarView.animate(slideReset);
setTimeout(function() {
thisCalendarView.left = (screenWidth * -1) + 'dp';
if (needToChangeSize == false) {
nextCalendarView.left = '-1dp';
} else {
nextCalendarView.left = ((screenWidth - 644) / 2);
}
prevCalendarView = thisCalendarView;
thisCalendarView = nextCalendarView;
if (b == 11) {
nextCalendarView = calView(a + 1, 0, c);
} else {
nextCalendarView = calView(a, b + 1, c);
}
monthTitle.text = monthName(b) + ' ' + a;
nextCalendarView.left = screenWidth + 'dp';
win.add(nextCalendarView);
}, 500);
});
// Previous Month Click Event
prevMonth.addEventListener('click', function() {
if (b == 0) {
b = 11;
a--;
} else {
b--;
}
thisCalendarView.animate(slidePrev);
prevCalendarView.animate(slideReset);
setTimeout(function() {
thisCalendarView.left = screenWidth + 'dp';
if (needToChangeSize == false) {
prevCalendarView.left = '-1dp';
} else {
prevCalendarView.left = ((screenWidth - 644) / 2);
}
nextCalendarView = thisCalendarView;
thisCalendarView = prevCalendarView;
if (b == 0) {
prevCalendarView = calView(a - 1, 11, c);
} else {
prevCalendarView = calView(a, b - 1, c);
}
monthTitle.text = monthName(b) + ' ' + a;
prevCalendarView.left = (screenWidth * -1) + 'dp';
win.add(prevCalendarView);
}, 500);
});
Tested Environment
Titanium Studio - 2.1.2.201208301612
Titanium SDK - 2.1.0.GA
Android - 2.2 Emulator
iOS Version - 5.0
Reference - http://titaniumexplorers.blogspot.in/2012/09/titanium-calendar-month-view.html
Related
I have this function type() where I define an array texts and I add some values to it and then I set the local texts array to the state array, so it'll be accessible for other functions as well.
var texts = [];
texts.push("Hello", 20, 20)
this.setState({texts: texts})
And this is the state:
state ={
texts:[]
}
I check with the alert() what's the index of 0 of the array to see if it's working, however it says it's undefined:
alert(this.state.texts[0])
This is my whole function:
type()
{
var canvas = document.getElementById('board'),
ctx = canvas.getContext('2d'),
font = '20px sans-serif',
hasInput = false;
var sketch = document.querySelector('#sketch');
var sketch_style = getComputedStyle(sketch);
canvas.width = parseInt(sketch_style.getPropertyValue('width'));
canvas.height = parseInt(sketch_style.getPropertyValue('height'));
var texts = [];
texts.push("Hello", 20, 20)
this.setState({texts: texts})
alert(this.state.texts[0])
ctx.font = font
ctx.fillText("rrwerwewewrewrwerwe", 50, 50)
canvas.onclick = function(e) {
if (hasInput) return;
addInput(e.clientX, e.clientY);
}
//Function to dynamically add an input box:
function addInput(x, y) {
var input = document.createElement('input');
input.type = 'text';
input.style.position = 'fixed';
input.style.left = (x - 4) + 'px';
input.style.top = (y - 4) + 'px';
input.onkeydown = handleEnter;
document.body.appendChild(input);
input.focus();
hasInput = true;
}
//Key handler for input box:
function handleEnter(e) {
var keyCode = e.keyCode;
if (keyCode === 13) {
addText(this.value, parseInt(this.style.left, 10), parseInt(this.style.top, 10));
document.body.removeChild(this);
hasInput = false;
}
}
//Draw the text onto canvas:
function addText(text, x, y) {
ctx.position = 'absolute';
ctx.font = font;
ctx.fillText(text, x, y);
}
}
I have created WordCloud from angularjs using wordCloud directive,
So whenever I change the input to the cloud after generating word cloud for the first time, I am unable form word cloud.
This issue is being, I am not able to refresh the directive.
Say my html and js is ,
var app = angular.module("myApp", ["tangcloud"]);
app.controller("myCtrl", function($scope) {
$scope.words = [
{id: 1, word: "oke", size: 1},
{id: 2, word: "blabla", size: 6},
{id: 3, word: "test", size: 7},
{id: 4, word: "schaap", size: 2},
{id: 5, word: "deployment", size: 10},
{id: 6, word: "woord3", size: 3},
{id: 7, word: "wogamalord4", size: 4}];
});
<tang-cloud words="words" on-click="test(word)"></tang-cloud>
Say my directive going to be
angular.module('tangcloud', [])
.directive('tangCloud', ['$interpolate', '$compile', '$timeout', function ($interpolate, $compile, $timeout) {
var directive = {
restrict: 'E',
scope: {
words: '=',
onClick: '&',
spin: '='
},
template: function (tElement, tAttrs) {
var isClickable = angular.isDefined(tAttrs.onClick);
var clickAttr = isClickable ? 'ng-click="onClick({word : entry.word, id : entry.id})"' : '';
return "<div class='tangcloud'>" +
"<span ng-repeat='entry in words'" + clickAttr + ">{{entry.word}}</span>" +
"</div>";
},
compile: function (elem) {
elem.children().children()
.addClass('tangcloud-item-' + $interpolate.startSymbol() + 'entry.size' + $interpolate.endSymbol())
.addClass('tangcloud-item-hidden');
return function (scope, elem) {
var div = elem.children().eq(0)[0];
scope.width = div.offsetWidth;
scope.height = div.offsetHeight;
var centerX = scope.width / 2;
var centerY = scope.height / 2;
var outOfBoundsCount = 0;
var takenSpots = [];
if (scope.words) {
scope.words = shuffleWords(scope.words);
determineWordPositions();
}
function shuffleWords(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
function determineWordPositions() {
$timeout(function () {
var trendSpans = elem.children().eq(0).children();
var length = trendSpans.length;
for (var i = 0; i < length; i++) {
setWordSpanPosition(trendSpans.eq(i));
}
});
}
function setWordSpanPosition(span) {
var height = parseInt(window.getComputedStyle(span[0]).lineHeight, 10);
var width = span[0].offsetWidth;
var spot = setupDefaultSpot(width, height);
var angleMultiplier = 0;
while (spotNotUsable(spot) && outOfBoundsCount < 50) {
spot = moveSpotOnSpiral(spot, angleMultiplier);
angleMultiplier += 1;
}
if (outOfBoundsCount < 50) {
takenSpots.push(spot);
addSpanPositionStyling(span, spot.startX, spot.startY);
}
outOfBoundsCount = 0;
}
function setupDefaultSpot(width, height) {
return {
width: width,
height: height,
startX: centerX - width / 2,
startY: centerY - height / 2,
endX: centerX + width / 2,
endY: centerY + height / 2
};
}
function moveSpotOnSpiral(spot, angleMultiplier) {
var angle = angleMultiplier * 0.1;
spot.startX = centerX + (1.5 * angle) * Math.cos(angle) - (spot.width / 2);
spot.startY = centerY + angle * Math.sin(angle) - (spot.height / 2);
spot.endX = spot.startX + spot.width;
spot.endY = spot.startY + spot.height;
return spot;
}
function spotNotUsable(spot) {
var borders = {
left: centerX - scope.width / 2,
right: centerX + scope.width / 2,
bottom: centerY - scope.height / 2,
top: centerY + scope.height / 2
};
for (var i = 0; i < takenSpots.length; i++) {
if (spotOutOfBounds(spot, borders) || collisionDetected(spot, takenSpots[i])) return true;
}
return false;
}
function spotOutOfBounds(spot, borders) {
if (spot.startX < borders.left ||
spot.endX > borders.right ||
spot.startY < borders.bottom ||
spot.endY > borders.top) {
outOfBoundsCount++;
return true;
} else {
return false;
}
}
function collisionDetected(spot, takenSpot) {
if (spot.startX > takenSpot.endX || spot.endX < takenSpot.startX) {
return false;
}
return !(spot.startY > takenSpot.endY || spot.endY < takenSpot.startY);
}
function addSpanPositionStyling(span, startX, startY) {
var style = "position: absolute; left:" + startX + "px; top: " + startY + "px;";
span.attr("style", style);
span.removeClass("tangcloud-item-hidden");
}
};
}
};
return directive;
}]);
How can I refresh my directive on change the input for my word-cloud
hi i need to create an ionic app to draw on image or pdf.
I need to drag the canvas where i want on the image, draw on it, resize it and save in another image wich is the result of the old one plus the draw canvas.
canvas is like this, now i can drag or i can draw, but i'm not capable to set 4 anchor on the edge of canvas to resize it
canvas ion-pinch overflow-scroll="false" id='signatureCanvas' on-drag="true" width="200" height="300" style='border: 1px solid black; background-color: transparent;'
the directive to drag
.directive('ionPinch', function($timeout,$ionicGesture) {
return {
restrict: 'A',
link: function($scope, $element) {
$timeout(function() {
var canvas = $element[0],
posX = 0,
posY = 0,
lastPosX = 0,
lastPosY = 0,
bufferX = 0,
bufferY = 0,
scale = 1,
lastScale,
rotation = 0,
last_rotation, dragReady = 0;
ionic.onGesture('touch drag transform dragend', function(e) {
e.gesture.srcEvent.preventDefault();
e.gesture.preventDefault();
switch (e.type) {
case 'touch':
lastScale = scale;
last_rotation = rotation;
break;
case 'drag':
posX = e.gesture.deltaX + lastPosX;
posY = e.gesture.deltaY + lastPosY;
break;
case 'transform':
rotation = e.gesture.rotation + last_rotation;
scale = e.gesture.scale * lastScale
break;
case 'dragend':
lastPosX = posX;
lastPosY = posY;
lastScale = scale;
break;
}
var transform =
"translate3d(" + posX + "px," + posY + "px, 0) " +
"scale(" + scale + ")" +
"rotate(" + rotation + "deg) ";
e.target.style.transform = transform;
e.target.style.webkitTransform = transform;
}, $element[0]);
});
}
};
})
and the controller to draw
ratio = 1.0;
$scope.dev_width = $window.innerWidth;
$scope.dev_height = $window.innerHeight;
var dev_width = $window.innerWidth;
var dev_height = $window.innerHeight;
console.log(dev_width);
var canvas = document.getElementById('signatureCanvas');
var context = canvas.getContext('2d');
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = canvas.offsetWidth * ratio;
canvas.height = canvas.offsetHeight * ratio;
canvas.getContext('2d').scale(ratio, ratio);
}
resizeCanvas();
var signaturePad = new SignaturePad(canvas);
$scope.clearCanvas = function() {
signaturePad.clear();
}
Can anybody tell me how to give a different field for a label and legend in pie chart in extjs? I have a store with two fields with name,value. I want to display name field in legend and value field in label. Now I am getting the same value for both labe1 and legend.
Thanks
Configure the label property of your pie serie like this:
label: {
// name of the model field to use as legend
field: 'name'
,renderer: function(value, label, storeItem) {
// storeItem is your model, so return the value you want as label
return storeItem.get('value');
}
}
Edit:
The previous code only works from since Ext 4.2. To use it with 4.1, you'd have to override Ext.chart.series.Pie#onPlaceLabel() method to replace the lines:
label.setAttributes({
text: format(storeItem.get(field[index]))
}, true);
With these, from Ext4.2 code:
label.setAttributes({
text: format(storeItem.get(field), label, storeItem, item, i, display, animate, index)
}, true);
Edit 2: How to override for Ext 4.1
The syntax for overriding a class in Ext 4 is the same as the one for extending, except that you use override instead of extend. The override class is named, like any other class. In order for the override to be loaded, you have to require it, exactly like a normal class (if you don't use Ext Loader, then you must include the file in a script tag.
Now, here's the code that will make the value renderer works in the same way as Ext4.2:
// Again, you're free to chose the class name, just ensure the Loader can find the file
// (and don't forget to require it!)
Ext.define('MyApp.Ext.char.series.Pie', {
override: 'Ext.chart.series.Pie'
,onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
var me = this,
chart = me.chart,
resizing = chart.resizing,
config = me.label,
format = config.renderer,
field = [].concat(config.field),
centerX = me.centerX,
centerY = me.centerY,
middle = item.middle,
opt = {
x: middle.x,
y: middle.y
},
x = middle.x - centerX,
y = middle.y - centerY,
from = {},
rho = 1,
theta = Math.atan2(y, x || 1),
dg = theta * 180 / Math.PI,
prevDg;
opt.hidden = false;
if (this.__excludes && this.__excludes[i]) {
opt.hidden = true;
}
function fixAngle(a) {
if (a < 0) {
a += 360;
}
return a % 360;
}
label.setAttributes({
// Removed:
// text: format(storeItem.get(field[index]))
// Added:
text: format(storeItem.get(config.field), label, storeItem, item, i, display, animate, index)
}, true);
switch (display) {
case 'outside':
rho = Math.sqrt(x * x + y * y) * 2;
//update positions
opt.x = rho * Math.cos(theta) + centerX;
opt.y = rho * Math.sin(theta) + centerY;
break;
case 'rotate':
dg = fixAngle(dg);
dg = (dg > 90 && dg < 270) ? dg + 180: dg;
prevDg = label.attr.rotation.degrees;
if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
if (dg > prevDg) {
dg -= 360;
} else {
dg += 360;
}
dg = dg % 360;
} else {
dg = fixAngle(dg);
}
//update rotation angle
opt.rotate = {
degrees: dg,
x: opt.x,
y: opt.y
};
break;
default:
break;
}
//ensure the object has zero translation
opt.translate = {
x: 0, y: 0
};
if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
me.onAnimate(label, {
to: opt
});
} else {
label.setAttributes(opt, true);
}
label._from = from;
}
});
As you can see, I had to copy-paste the whole method code. I don't like that because that makes us largely exposed to any code change in future version, however there is no other possible way to change the bit of code I'm targeting.
In real life, in order to safeguard against this risk, I would add a version check and issue a warning if Ext version is greater than 4.1:
// Using a function instead of a raw object, in order to run some code at the time of class definition
Ext.define('MyApp.Ext.char.series.Pie', function() {
if (Ext.getVersion().isGreaterThanOrEqual('4.2')) {
Ext.Logger.warn('This override is rendered useless since Ext4.2');
return {};
} else {
return {
override: 'Ext.chart.series.Pie'
,onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {
var me = this,
chart = me.chart,
resizing = chart.resizing,
config = me.label,
format = config.renderer,
field = [].concat(config.field),
centerX = me.centerX,
centerY = me.centerY,
middle = item.middle,
opt = {
x: middle.x,
y: middle.y
},
x = middle.x - centerX,
y = middle.y - centerY,
from = {},
rho = 1,
theta = Math.atan2(y, x || 1),
dg = theta * 180 / Math.PI,
prevDg;
opt.hidden = false;
if (this.__excludes && this.__excludes[i]) {
opt.hidden = true;
}
function fixAngle(a) {
if (a < 0) {
a += 360;
}
return a % 360;
}
label.setAttributes({
// Removed:
// text: format(storeItem.get(field[index]))
// Added:
text: format(storeItem.get(config.field), label, storeItem, item, i, display, animate, index)
}, true);
switch (display) {
case 'outside':
rho = Math.sqrt(x * x + y * y) * 2;
//update positions
opt.x = rho * Math.cos(theta) + centerX;
opt.y = rho * Math.sin(theta) + centerY;
break;
case 'rotate':
dg = fixAngle(dg);
dg = (dg > 90 && dg < 270) ? dg + 180: dg;
prevDg = label.attr.rotation.degrees;
if (prevDg != null && Math.abs(prevDg - dg) > 180 * 0.5) {
if (dg > prevDg) {
dg -= 360;
} else {
dg += 360;
}
dg = dg % 360;
} else {
dg = fixAngle(dg);
}
//update rotation angle
opt.rotate = {
degrees: dg,
x: opt.x,
y: opt.y
};
break;
default:
break;
}
//ensure the object has zero translation
opt.translate = {
x: 0, y: 0
};
if (animate && !resizing && (display != 'rotate' || prevDg != null)) {
me.onAnimate(label, {
to: opt
});
} else {
label.setAttributes(opt, true);
}
label._from = from;
}
};
}
}()); // the define function must be executed manually with overrides (this is documented in the API)
I don't know whether it is against the copyright law or not
and i am not responsible anything you do. but it will work
step 1
open touch/src/chart/series/pie.js file
step 2
add one property to this file (like legendLabel:null,)
step 3
Edit method provideLegendInfo: function (target) . and change
labelField = this.getLabelField(), to
labelField = this.getLegendLabel()
step 4<br>
Add new property to series you create in your file to show pie chart
series: [
{
type: 'pie',
legendLabel :'name',
xField: 'value',
labelField: 'value',
}]
promote this answer if you find this is helpful
thank u
this should works for Ext 4.0
new Ext.chart.Chart({
store: store,
series: [{
type: 'pie',
field: 'value',
label: {
field: 'name',
renderer: function(value){
Ext.each(store.data.items, function(item){
if(item.data.name == value) {
value = item.data.value;
}
});
return value;
}
}
}],
...
store data looks like:
[{"name":"series1","value":5},{"name":"series2","value":7}]
I have a way to show two different labels on legend and PIE separately using rendererFn (sprites). Here you don't need to edit the JS files.
In the "series" section, Under the Label category, use the "field" to display the LEGEND text and in the renderer function use values as output to return the values on to the pie.
series: [{
type: 'pie',
field: 'value',
donut: 25,
showInLegend: true,
label: {
field: 'name',
renderer: {rendererFn},
display: 'inside'
}
,renderer: function(sprite, config, rendererData, index) {
return { text: {rendererFn}(window.dataJSON[index]['value']) };
}
}]
This would suffice.
We are using ExtJS4 in our application.But we are facing an issue with hbox layout.We need to displat the items from right side.Normally in ExtJS4,items in hbox layout start from left side and move towards right side.But we need to start from right side and move towards left side.I think we need to change the order in ExtJS4 library(box layout).
ExtJS4 box layout is:
/*
This file is part of Ext JS 4
Copyright (c) 2011 Sencha Inc
Contact: http://www.sencha.com/contact
GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
*/
Ext.define('Ext.layout.container.Box', {
alias: ['layout.box'],
extend: 'Ext.layout.container.Container',
alternateClassName: 'Ext.layout.BoxLayout'
requires: [
'Ext.layout.container.boxOverflow.None',
'Ext.layout.container.boxOverflow.Menu',
'Ext.layout.container.boxOverflow.Scroller',
'Ext.util.Format',
'Ext.dd.DragDropManager'
],
defaultMargins: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
padding: '0',
type: 'box',
scrollOffset: 0,
itemCls: Ext.baseCSSPrefix + 'box-item',
targetCls: Ext.baseCSSPrefix + 'box-layout-ct',
innerCls: Ext.baseCSSPrefix + 'box-inner',
bindToOwnerCtContainer: true,
fixedLayout: false,
availableSpaceOffset: 0,
reserveOffset: true,
clearInnerCtOnLayout: false,
flexSortFn: function (a, b) {
var maxParallelPrefix = 'max' + this.parallelPrefixCap,
infiniteValue = Infinity;
a = a.component[maxParallelPrefix] || infiniteValue;
b = b.component[maxParallelPrefix] || infiniteValue;
// IE 6/7 Don't like Infinity - Infinity...
if (!isFinite(a) && !isFinite(b)) {
return false;
}
return a - b;
},
// Sort into *descending* order.
minSizeSortFn: function(a, b) {
return b.available - a.available;
},
constructor: function(config) {
var me = this;
me.callParent(arguments);
me.flexSortFn = Ext.Function.bind(me.flexSortFn, me);
me.initOverflowHandler();
},
getChildBox: function(child) {
child = child.el || this.owner.getComponent(child).el;
return {
left: child.getLeft(true),
top: child.getTop(true),
width: child.getWidth(),
height: child.getHeight()
};
},
calculateChildBox: function(child) {
var me = this,
boxes = me.calculateChildBoxes(me.getVisibleItems(), me.getLayoutTargetSize()).boxes,
ln = boxes.length,
i = 0;
child = me.owner.getComponent(child);
for (; i < ln; i++) {
if (boxes[i].component === child) {
return boxes[i];
}
}
},
calculateChildBoxes: function(visibleItems, targetSize) {
var me = this,
math = Math,
mmax = math.max,
infiniteValue = Infinity,
undefinedValue,
parallelPrefix = me.parallelPrefix,
parallelPrefixCap = me.parallelPrefixCap,
perpendicularPrefix = me.perpendicularPrefix,
perpendicularPrefixCap = me.perpendicularPrefixCap,
parallelMinString = 'min' + parallelPrefixCap,
perpendicularMinString = 'min' + perpendicularPrefixCap,
perpendicularMaxString = 'max' + perpendicularPrefixCap,
parallelSize = targetSize[parallelPrefix] - me.scrollOffset,
perpendicularSize = targetSize[perpendicularPrefix],
padding = me.padding,
parallelOffset = padding[me.parallelBefore],
paddingParallel = parallelOffset + padding[me.parallelAfter],
perpendicularOffset = padding[me.perpendicularLeftTop],
paddingPerpendicular = perpendicularOffset + padding[me.perpendicularRightBottom],
availPerpendicularSize = mmax(0, perpendicularSize - paddingPerpendicular),
isStart = me.pack == 'start',
isCenter = me.pack == 'center',
isEnd = me.pack == 'end',
constrain = Ext.Number.constrain,
visibleCount = visibleItems.length,
nonFlexSize = 0,
totalFlex = 0,
desiredSize = 0,
minimumSize = 0,
maxSize = 0,
boxes = [],
minSizes = [],
calculatedWidth,
i, child, childParallel, childPerpendicular, childMargins, childSize, minParallel, tmpObj, shortfall,
tooNarrow, availableSpace, minSize, item, length, itemIndex, box, oldSize, newSize, reduction, diff,
flexedBoxes, remainingSpace, remainingFlex, flexedSize, parallelMargins, calcs, offset,
perpendicularMargins, stretchSize;
for (i = 0; i < visibleCount; i++) {
child = visibleItems[i];
childPerpendicular = child[perpendicularPrefix];
me.layoutItem(child);
childMargins = child.margins;
parallelMargins = childMargins[me.parallelBefore] + childMargins[me.parallelAfter];
tmpObj = {
component: child,
margins: childMargins
};
// flex and not 'auto' width
if (child.flex) {
totalFlex += child.flex;
childParallel = undefinedValue;
}
// Not flexed or 'auto' width or undefined width
else {
if (!(child[parallelPrefix] && childPerpendicular)) {
childSize = child.getSize();
}
childParallel = child[parallelPrefix] || childSize[parallelPrefix];
childPerpendicular = childPerpendicular || childSize[perpendicularPrefix];
}
nonFlexSize += parallelMargins + (childParallel || 0);
desiredSize += parallelMargins + (child.flex ? child[parallelMinString] || 0 : childParallel);
minimumSize += parallelMargins + (child[parallelMinString] || childParallel || 0);
// Max height for align - force layout of non-laid out subcontainers without a numeric height
if (typeof childPerpendicular != 'number') {
// Clear any static sizing and revert to flow so we can get a proper measurement
childPerpendicular = child['get' + perpendicularPrefixCap]();
}
// Track the maximum perpendicular size for use by the stretch and stretchmax align config values.
maxSize = mmax(maxSize, childPerpendicular + childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom]);
tmpObj[parallelPrefix] = childParallel || undefinedValue;
tmpObj[perpendicularPrefix] = childPerpendicular || undefinedValue;
boxes.push(tmpObj);
}
shortfall = desiredSize - parallelSize;
tooNarrow = minimumSize > parallelSize;
//the space available to the flexed items
availableSpace = mmax(0, parallelSize - nonFlexSize - paddingParallel - (me.reserveOffset ? me.availableSpaceOffset : 0));
if (tooNarrow) {
for (i = 0; i < visibleCount; i++) {
box = boxes[i];
minSize = visibleItems[i][parallelMinString] || visibleItems[i][parallelPrefix] || box[parallelPrefix];
box.dirtySize = box.dirtySize || box[parallelPrefix] != minSize;
box[parallelPrefix] = minSize;
}
}
else {
if (shortfall > 0) {
for (i = 0; i < visibleCount; i++) {
item = visibleItems[i];
minSize = item[parallelMinString] || 0;
if (item.flex) {
box = boxes[i];
box.dirtySize = box.dirtySize || box[parallelPrefix] != minSize;
box[parallelPrefix] = minSize;
}
else {
minSizes.push({
minSize: minSize,
available: boxes[i][parallelPrefix] - minSize,
index: i
});
}
}
Ext.Array.sort(minSizes, me.minSizeSortFn);
for (i = 0, length = minSizes.length; i < length; i++) {
itemIndex = minSizes[i].index;
if (itemIndex == undefinedValue) {
continue;
}
item = visibleItems[itemIndex];
minSize = minSizes[i].minSize;
box = boxes[itemIndex];
oldSize = box[parallelPrefix];
newSize = mmax(minSize, oldSize - math.ceil(shortfall / (length - i)));
reduction = oldSize - newSize;
box.dirtySize = box.dirtySize || box[parallelPrefix] != newSize;
box[parallelPrefix] = newSize;
shortfall -= reduction;
}
}
else {
remainingSpace = availableSpace;
remainingFlex = totalFlex;
flexedBoxes = [];
for (i = 0; i < visibleCount; i++) {
child = visibleItems[i];
if (isStart && child.flex) {
flexedBoxes.push(boxes[Ext.Array.indexOf(visibleItems, child)]);
}
}
Ext.Array.sort(flexedBoxes, me.flexSortFn);
for (i = 0; i < flexedBoxes.length; i++) {
calcs = flexedBoxes[i];
child = calcs.component;
childMargins = calcs.margins;
flexedSize = math.ceil((child.flex / remainingFlex) * remainingSpace);
flexedSize = Math.max(child['min' + parallelPrefixCap] || 0, math.min(child['max' + parallelPrefixCap] || infiniteValue, flexedSize));
remainingSpace -= flexedSize;
remainingFlex -= child.flex;
calcs.dirtySize = calcs.dirtySize || calcs[parallelPrefix] != flexedSize;
calcs[parallelPrefix] = flexedSize;
}
}
}
if (isCenter) {
parallelOffset += availableSpace / 2;
}
else if (isEnd) {
parallelOffset += availableSpace;
}
if (me.owner.dock && (Ext.isIE6 || Ext.isIE7 || Ext.isIEQuirks) && !me.owner.width && me.direction == 'vertical') {
calculatedWidth = maxSize + me.owner.el.getPadding('lr') + me.owner.el.getBorderWidth('lr');
if (me.owner.frameSize) {
calculatedWidth += me.owner.frameSize.left + me.owner.frameSize.right;
}
availPerpendicularSize = Math.min(availPerpendicularSize, targetSize.width = maxSize + padding.left + padding.right);
}
//finally, calculate the left and top position of each item
for (i = 0; i < visibleCount; i++) {
child = visibleItems[i];
calcs = boxes[i];
childMargins = calcs.margins;
perpendicularMargins = childMargins[me.perpendicularLeftTop] + childMargins[me.perpendicularRightBottom];
parallelOffset += childMargins[me.parallelBefore];
calcs[me.parallelBefore] = parallelOffset;
calcs[me.perpendicularLeftTop] = perpendicularOffset + childMargins[me.perpendicularLeftTop];
if (me.align == 'stretch') {
stretchSize = constrain(availPerpendicularSize - perpendicularMargins, child[perpendicularMinString] || 0, child[perpendicularMaxString] || infiniteValue);
calcs.dirtySize = calcs.dirtySize || calcs[perpendicularPrefix] != stretchSize;
calcs[perpendicularPrefix] = stretchSize;
}
else if (me.align == 'stretchmax') {
stretchSize = constrain(maxSize - perpendicularMargins, child[perpendicularMinString] || 0, child[perpendicularMaxString] || infiniteValue);
calcs.dirtySize = calcs.dirtySize || calcs[perpendicularPrefix] != stretchSize;
calcs[perpendicularPrefix] = stretchSize;
}
else if (me.align == me.alignCenteringString) {
// When calculating a centered position within the content box of the innerCt, the width of the borders must be subtracted from
// the size to yield the space available to center within.
// The updateInnerCtSize method explicitly adds the border widths to the set size of the innerCt.
diff = mmax(availPerpendicularSize, maxSize) - me.innerCt.getBorderWidth(me.perpendicularLT + me.perpendicularRB) - calcs[perpendicularPrefix];
if (diff > 0) {
calcs[me.perpendicularLeftTop] = perpendicularOffset + Math.round(diff / 2);
}
}
// Advance past the box size and the "after" margin
parallelOffset += (calcs[parallelPrefix] || 0) + childMargins[me.parallelAfter];
}
return {
boxes: boxes,
meta : {
calculatedWidth: calculatedWidth,
maxSize: maxSize,
nonFlexSize: nonFlexSize,
desiredSize: desiredSize,
minimumSize: minimumSize,
shortfall: shortfall,
tooNarrow: tooNarrow
}
};
},
onRemove: function(comp){
this.callParent(arguments);
if (this.overflowHandler) {
this.overflowHandler.onRemove(comp);
}
},
initOverflowHandler: function() {
var handler = this.overflowHandler;
if (typeof handler == 'string') {
handler = {
type: handler
};
}
var handlerType = 'None';
if (handler && handler.type !== undefined) {
handlerType = handler.type;
}
var constructor = Ext.layout.container.boxOverflow[handlerType];
if (constructor[this.type]) {
constructor = constructor[this.type];
}
this.overflowHandler = Ext.create('Ext.layout.container.boxOverflow.' + handlerType, this, handler);
},
onLayout: function() {
this.callParent();
// Clear the innerCt size so it doesn't influence the child items.
if (this.clearInnerCtOnLayout === true && this.adjustmentPass !== true) {
this.innerCt.setSize(null, null);
}
var me = this,
targetSize = me.getLayoutTargetSize(),
items = me.getVisibleItems(),
calcs = me.calculateChildBoxes(items, targetSize),
boxes = calcs.boxes,
meta = calcs.meta,
handler, method, results;
if (me.autoSize && calcs.meta.desiredSize) {
targetSize[me.parallelPrefix] = calcs.meta.desiredSize;
}
//invoke the overflow handler, if one is configured
if (meta.shortfall > 0) {
handler = me.overflowHandler;
method = meta.tooNarrow ? 'handleOverflow': 'clearOverflow';
results = handler[method](calcs, targetSize);
if (results) {
if (results.targetSize) {
targetSize = results.targetSize;
}
if (results.recalculate) {
items = me.getVisibleItems(owner);
calcs = me.calculateChildBoxes(items, targetSize);
boxes = calcs.boxes;
}
}
} else {
me.overflowHandler.clearOverflow();
}
me.layoutTargetLastSize = targetSize;
me.childBoxCache = calcs;
me.updateInnerCtSize(targetSize, calcs);
me.updateChildBoxes(boxes);
me.handleTargetOverflow(targetSize);
},
updateChildBoxes: function(boxes) {
var me = this,
i = 0,
length = boxes.length,
animQueue = [],
dd = Ext.dd.DDM.getDDById(me.innerCt.id), // Any DD active on this layout's element (The BoxReorderer plugin does this.)
oldBox, newBox, changed, comp, boxAnim, animCallback;
for (; i < length; i++) {
newBox = boxes[i];
comp = newBox.component;
// If a Component is being drag/dropped, skip positioning it.
// Accomodate the BoxReorderer plugin: Its current dragEl must not be positioned by the layout
if (dd && (dd.getDragEl() === comp.el.dom)) {
continue;
}
changed = false;
oldBox = me.getChildBox(comp);
// If we are animating, we build up an array of Anim config objects, one for each
// child Component which has any changed box properties. Those with unchanged
// properties are not animated.
if (me.animate) {
// Animate may be a config object containing callback.
animCallback = me.animate.callback || me.animate;
boxAnim = {
layoutAnimation: true, // Component Target handler must use set*Calculated*Size
target: comp,
from: {},
to: {},
listeners: {}
};
// Only set from and to properties when there's a change.
// Perform as few Component setter methods as possible.
// Temporarily set the property values that we are not animating
// so that doComponentLayout does not auto-size them.
if (!isNaN(newBox.width) && (newBox.width != oldBox.width)) {
changed = true;
// boxAnim.from.width = oldBox.width;
boxAnim.to.width = newBox.width;
}
if (!isNaN(newBox.height) && (newBox.height != oldBox.height)) {
changed = true;
// boxAnim.from.height = oldBox.height;
boxAnim.to.height = newBox.height;
}
if (!isNaN(newBox.left) && (newBox.left != oldBox.left)) {
changed = true;
// boxAnim.from.left = oldBox.left;
boxAnim.to.left = newBox.left;
}
if (!isNaN(newBox.top) && (newBox.top != oldBox.top)) {
changed = true;
// boxAnim.from.top = oldBox.top;
boxAnim.to.top = newBox.top;
}
if (changed) {
animQueue.push(boxAnim);
}
} else {
if (newBox.dirtySize) {
if (newBox.width !== oldBox.width || newBox.height !== oldBox.height) {
me.setItemSize(comp, newBox.width, newBox.height);
}
}
// Don't set positions to NaN
if (isNaN(newBox.left) || isNaN(newBox.top)) {
continue;
}
comp.setPosition(newBox.left, newBox.top);
}
}
// Kick off any queued animations
length = animQueue.length;
if (length) {
// A function which cleans up when a Component's animation is done.
// The last one to finish calls the callback.
var afterAnimate = function(anim) {
// When we've animated all changed boxes into position, clear our busy flag and call the callback.
length -= 1;
if (!length) {
me.layoutBusy = false;
if (Ext.isFunction(animCallback)) {
animCallback();
}
}
};
var beforeAnimate = function() {
me.layoutBusy = true;
};
// Start each box animation off
for (i = 0, length = animQueue.length; i < length; i++) {
boxAnim = animQueue[i];
// Clean up the Component after. Clean up the *layout* after the last animation finishes
boxAnim.listeners.afteranimate = afterAnimate;
// The layout is busy during animation, and may not be called, so set the flag when the first animation begins
if (!i) {
boxAnim.listeners.beforeanimate = beforeAnimate;
}
if (me.animate.duration) {
boxAnim.duration = me.animate.duration;
}
comp = boxAnim.target;
delete boxAnim.target;
// Stop any currently running animation
comp.stopAnimation();
comp.animate(boxAnim);
}
}
},
updateInnerCtSize: function(tSize, calcs) {
var me = this,
mmax = Math.max,
align = me.align,
padding = me.padding,
width = tSize.width,
height = tSize.height,
meta = calcs.meta,
innerCtWidth,
innerCtHeight;
if (me.direction == 'horizontal') {
innerCtWidth = width;
innerCtHeight = meta.maxSize + padding.top + padding.bottom + me.innerCt.getBorderWidth('tb');
if (align == 'stretch') {
innerCtHeight = height;
}
else if (align == 'middle') {
innerCtHeight = mmax(height, innerCtHeight);
}
} else {
innerCtHeight = height;
innerCtWidth = meta.maxSize + padding.left + padding.right + me.innerCt.getBorderWidth('lr');
if (align == 'stretch') {
innerCtWidth = width;
}
else if (align == 'center') {
innerCtWidth = mmax(width, innerCtWidth);
}
}
me.getRenderTarget().setSize(innerCtWidth || undefined, innerCtHeight || undefined);
// If a calculated width has been found (and this only happens for auto-width vertical docked Components in old Microsoft browsers)
// then, if the Component has not assumed the size of its content, set it to do so.
if (meta.calculatedWidth && me.owner.el.getWidth() > meta.calculatedWidth) {
me.owner.el.setWidth(meta.calculatedWidth);
}
if (me.innerCt.dom.scrollTop) {
me.innerCt.dom.scrollTop = 0;
}
},
handleTargetOverflow: function(previousTargetSize) {
var target = this.getTarget(),
overflow = target.getStyle('overflow'),
newTargetSize;
if (overflow && overflow != 'hidden' && !this.adjustmentPass) {
newTargetSize = this.getLayoutTargetSize();
if (newTargetSize.width != previousTargetSize.width || newTargetSize.height != previousTargetSize.height) {
this.adjustmentPass = true;
this.onLayout();
return true;
}
}
delete this.adjustmentPass;
},
// private
isValidParent : function(item, target, position) {
// Note: Box layouts do not care about order within the innerCt element because it's an absolutely positioning layout
// We only care whether the item is a direct child of the innerCt element.
var itemEl = item.el ? item.el.dom : Ext.getDom(item);
return (itemEl && this.innerCt && itemEl.parentNode === this.innerCt.dom) || false;
},
// Overridden method from AbstractContainer.
// Used in the base AbstractLayout.beforeLayout method to render all items into.
getRenderTarget: function() {
if (!this.innerCt) {
// the innerCt prevents wrapping and shuffling while the container is resizing
this.innerCt = this.getTarget().createChild({
cls: this.innerCls,
role: 'presentation'
});
this.padding = Ext.util.Format.parseBox(this.padding);
}
return this.innerCt;
},
// private
renderItem: function(item, target) {
this.callParent(arguments);
var me = this,
itemEl = item.getEl(),
style = itemEl.dom.style,
margins = item.margins || item.margin;
// Parse the item's margin/margins specification
if (margins) {
if (Ext.isString(margins) || Ext.isNumber(margins)) {
margins = Ext.util.Format.parseBox(margins);
} else {
Ext.applyIf(margins, {top: 0, right: 0, bottom: 0, left: 0});
}
} else {
margins = Ext.apply({}, me.defaultMargins);
}
// Add any before/after CSS margins to the configured margins, and zero the CSS margins
margins.top += itemEl.getMargin('t');
margins.right += itemEl.getMargin('r');
margins.bottom += itemEl.getMargin('b');
margins.left += itemEl.getMargin('l');
style.marginTop = style.marginRight = style.marginBottom = style.marginLeft = '0';
// Item must reference calculated margins.
item.margins = margins;
},
destroy: function() {
Ext.destroy(this.overflowHandler);
this.callParent(arguments);
}
});
Help would be appreciated.
You just need to set your layout config, like this:
Ext.create('Ext.panel.Panel', {
layout: {type: 'hbox', pack:'end'}
});
The key there is pack:
Controls how the child items of the container are packed together. Acceptable configuration values for this property are:
'start' : Default
child items are packed together at left side of container
'center' :
child items are packed together at mid-width of container
'end' :
child items are packed together at right side of container
Hope that helps