Radiogroup keyboard navigation - extjs

I have a radio group that is set in a table layout:
https://fiddle.sencha.com/#view/editor&fiddle/2ehg
If you navigate around that radio group with left, right, up and down keys, the behaviour is unintuitive. I have searched the code and tried to debug the focus event on the radios, but did not find how/where ExtJS code navigates through the components.
How can I improve the keyboard navigation such that it behaves natural?

With the keyMap config and listening on the navigation key events you can prevent the browser default, calculate the new position and set it.
See the working fiddle.
It reacts more than you would expect. When you are on the first item and you press the up-button the new focused item is the last one in the row.
You may have to tweak left and right accordingly to fit your needs.
// get the id for the component from the event
const getPureId = (event) => event.target.id.split('-').splice(0, 2).join('-');
const columnCount = 2;
const maxIndex = 5;
const minIndex = 0;
// config of the panel ...
keyMap : {
[Ext.event.Event.DOWN]: (event, panel) => {
var pureId = getPureId(event);
var comp = Ext.get(pureId).component;
var pos = panel.items.indexOf(comp);
var newPos = pos + columnCount;
// when the newPos is outside of our boundaries we calculate the new one
// based on the columnCount
if (newPos > maxIndex) {
newPos = newPos % (columnCount + 1);
}
event.preventDefault();
panel
.getComponent(newPos)
.focus()
.setValue(true);
}, [Ext.event.Event.UP]: (event, panel) => {
var pureId = getPureId(event);
var comp = Ext.get(pureId).component;
var pos = panel.items.indexOf(comp);
var newPos = pos - columnCount;
// when the newPos is outside of our boundaries we calculate the new one
// based on the maxIndex
if (newPos < minIndex) {
newPos = newPos + maxIndex + 1;
}
event.preventDefault();
panel
.getComponent(newPos)
.focus()
.setValue(true);
}
},
// more config of the panel ...

Related

How can I open a markercluster programmatically

I want to be able to click on a list and make the map flyTo the position of a marker and open its popup. I'm able to do so as long as the marker has an identical position (not spiderfied). I've made a script to find the markerCluster which contains the marker, but I can't trigger its click method, to make the marker accessable.
// if marker is not accessible
Object.values(mapRef.current._targets).forEach(_target => {
if (_target._markers) {
const wantedMarker = _target._markers.find(_marker => {
return (
_marker.options.id === someId
);
});
if (wantedMarker) {
_target.click() // _target.click() is not a function
Without a living code example, I can't confirm that wantedMarker is indeed the instance of the marker you want. But if its, .click() is indeed not a function on that. However, you can get the marker's popup, if there is one, and open that programatically:
const popup = wantedMarker.getPopup();
if (popup){
popup.openOn(map) // you'll need a reference to the L.map instance for this
}
Solved it by looking for "_icon" in all parent elements. Elements hidden by markerCluster does not have this property. The _icon element has click events, so I used that to open the marker clusters.
const openMarkerClustersAndMarkerPopup = (allMarkersRef, markerId) => {
const markerElementRef = allMarkersRef.current[markerId];
if (markerElementRef._icon) {
markerElementRef._icon.click();
} else {
const clusters = [];
let _currentElement = markerElementRef;
while (_currentElement.__parent) {
clusters.push(_currentElement);
if (_currentElement._icon) {
break;
}
_currentElement = _currentElement.__parent;
}
// Trigger markercluster and marker by starting with top markercluster
for (let i = clusters.length - 1; i >= 0; i--) {
const _icon = clusters[i]._icon;
_icon && _icon.click();
}
}
};

React Amchart - How to remove animation when loading charts

I am using amcharts in the React Hooks environment.
I'm using force-directed-tree.
https://www.amcharts.com/demos/force-directed-tree/
The data applied to the chart is filtered based on high values and then reapplying.
Every time the data is reapply, the chart is rendered and the animation continues to be applied.
How to remove animation when loading charts
Or we would appreciate it if you could tell us how to apply the scrollbar provided by amcharts to this chart.
useEffect(() => {
const chart = am4core.create('networkChart', am4plugins_forceDirected.ForceDirectedTree);
const networkSeries = chart.series.push(new am4plugins_forceDirected.ForceDirectedSeries());
chart.data = data;
chart.dataSource.updateCurrentData = true;
chart.animationPlayed = true;
chart.dataSource.reloadFrequency = 1000;
networkSeries.dataFields.id = 'name';
networkSeries.dataFields.value = 'value';
networkSeries.dataFields.name = 'name';
networkSeries.dataFields.children = 'children';
networkSeries.nodes.template.tooltipText = '{name}:{value}';
networkSeries.nodes.template.fillOpacity = 1;
networkSeries.manyBodyStrength = -20;
networkSeries.links.template.strength = 0.8;
networkSeries.links.template.distance = 1;
networkSeries.minRadius = am4core.percent(3);
networkSeries.maxRadius = am4core.percent(10);
networkSeries.nodes.template.label.text = '{name}';
networkSeries.fontSize = 12;
networkSeries.maxLevels = 4;
networkSeries.events.disableType('inited', function() {
networkSeries.animate(
{
property: 'velocityDecay',
to: 1,
},
3000,
);
});
// Add Legend
chart.legend = new am4charts.Legend();
chart.legend.labels.template.fill = am4core.color('#fff');
}, [data]);
I didn't want the these to be used, so I used the unuseTheme and passed the animation theme to it. This removed the animations with respect to the chart.
Example:
am4core.unuseTheme(am4themesAnimated);
To Add scrollbar
let chart = am4core.create("chartdiv", am4charts.XYChart);
...
let scrollbarX = new am4charts.XYChartScrollbar();
scrollbarX.series.push(networkSeries);
chart.scrollbarX = scrollbarX;
https://www.amcharts.com/docs/v4/tutorials/customizing-chart-scrollbar/

Fabric.js group/ungroup - individual objects disappear while ungrouping

I have this functions in my angularJS controller:
function group(){
var activegroup = canvas.getActiveGroup();
var objectsInGroup = activegroup.getObjects();
activegroup.clone(function(newgroup) {
canvas.discardActiveGroup();
objectsInGroup.forEach(function(object) {
canvas.remove(object);
});
canvas.add(newgroup);
});
}
function ungroup(){
var activeObject = canvas.getActiveObject();
if(activeObject.type=="group"){
var items = activeObject._objects;
activeObject._restoreObjectsState();
canvas.remove(activeObject);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
canvas.item(canvas.size()-1).hasControls = true;
}
}
canvas.renderAll();
}
Working almost perfectly - jus when I ungroup the objects are not rendered (they are still in the canvas, but not visible). I can select the objects individually even if they are invisible. Selectin multiple object will show them while they are selected – deselecting one will let the other disappear. In short: ungrouping objects will not show as individual objects again.
This applys only to primitives like, box, circle, triangle. Text an images are not affected.
Help is appreciated!
function ungroup(){
var activeObject = canvas.getActiveObject();
if(activeObject.type=="group"){
var items = activeObject._objects;
alert(items);
activeObject._restoreObjectsState();
canvas.remove(activeObject);
for(var i = 0; i < items.length; i++) {
canvas.add(items[i]);
items[i].dirty = true; //set object dirty true
canvas.item(canvas.size()-1).hasControls = true;
}
canvas.renderAll();
}
}
If you set object dirty true, it will render in renderAll() call and redraw again.

Actionscript 3.0 : Controlling child movieclips added with an array

I'm trying to build a generic slide show flash file controlled with key presses... right and left arrow to go to next / previous slide, down and up to call / uncall the single animations / bullet points / whatever on a single slide.
So far I managed to get a file that can load all the pages from a movieclip and flick through them with right and left arrow and a nice fade out and fade in effect, even when flicking very fast.
What I do not get at all after several hours and lots of google searches is how to now control the single page movieclips. I uploaded the file so far:
http://www.broesel-brzelius.de/zeug/slide.zip
You can see that my (as of yet crude) attempt to control the pageHolder.Pages.page1 movieclip to gotoAndStop(2) just results in the pageHolder.Pages movieclip that was loaded to position 0 in the array to go to frame 2 and thus display the second page.
Code so far:
import flash.display.Sprite;
import flash.events.MouseEvent;
import com.greensock.*;
import flash.display.MovieClip;
// instantiate a variable to find number of pages
var numberOfPages:Pages = new Pages();
// instantiate an array to hold the page movieclips
var pageArray:Array = new Array();
// instantiate a container to hold the pages
var pageHolder:Sprite = new Sprite();
// declare variables that will hold reference to the current page IDs
var targetIDold:int = 0;
var targetIDnew:int = 0;
// declare a variable that will hold the current direction of slide movement
var movement:int = -1;
// call a function that builds the application
// pass in the number of pages in fl_prevSlide
buildApp(numberOfPages.totalFrames);
// this function builds the application
function buildApp(n:int):void
{
// declare variables for the pages
var p:Pages;
// instantiate a new Page, send its playhead to the current value of i+1
// it is necessary to add 1 to the value of i, since i starts at 0, while the timeline starts at 1
for (var i:int = 0; i < n; i++)
{
p = new Pages();
p.gotoAndStop(i + 1);
pageArray.push(p);
}
// set the position of the pageHolder relative to the buttonHolder
pageHolder.x = pageHolder.y = 0;
// add the first page (at index 0) from pageArray to pageHolder
pageHolder.addChild(pageArray[targetIDnew]);
// add pageHolder and buttonHolder to the stage
addChild(pageHolder);
}
//Adding Listener and corresponding function to change between slides
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeSlide);
function f_changeSlide(evt:KeyboardEvent):void
{
if(evt.keyCode == 37) // left arrow
{
f_prevSlide();
}
else if (evt.keyCode == 39 || evt.keyCode == 32) // right arrow or space
{
f_nextSlide();
}
}
function f_prevSlide():void
{
if(targetIDnew > 0)
{
movement = -1;
targetIDnew -= 1;
f_addPage();
}
}
function f_nextSlide():void
{
if(targetIDnew < (numberOfPages.totalFrames - 1))
{
movement = 1;
targetIDnew += 1;
f_addPage();
}
}
function f_addPage():void
{
// use the targetID variable to access the corresponding index in the pageArray and assign it to a temporary variable
targetIDold = targetIDnew - movement;
var _mcOld:MovieClip = MovieClip(pageArray[targetIDold]);
var _mcNew:MovieClip = MovieClip(pageArray[targetIDnew]);
//avoid flickering
if (_mcNew.alpha == 1) {
_mcNew.alpha = 0;
}
// add the temp variable to pageHolder
pageHolder.addChild(_mcNew);
// tween the temp variable to the specified properties, then call a function to remove the previous page
// new page gets faded in, old page gets faded out
TweenMax.to(_mcNew, 1.5, {alpha:1});
TweenMax.to(_mcOld, 1.5, {alpha:0, onComplete:f_removePage});
}
function f_removePage():void
{
// previous page will always be at index 0; remove it
// the new page just added will drop down to index 0 when the previous page is removed
pageHolder.removeChildAt(0);
}
//Trying to advance / decrease the timeline in the movieclips of the single pages by pressing down / up
stage.addEventListener(KeyboardEvent.KEY_DOWN, f_changeFrame);
function f_changeFrame(evt:KeyboardEvent):void
{
if(evt.keyCode == 40) // down arrow
{
f_nextFrame();
}
else if (evt.keyCode == 38) // up arrow
{
f_prevFrame();
}
}
function f_nextFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame + 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("down!");
}
function f_prevFrame():void
{
var w:uint = pageHolder.numChildren - 1 ;
(pageHolder.getChildAt(w) as MovieClip).gotoAndStop(currentFrame - 1);
// for (var i:uint = 0; i < pageHolder.numChildren; i++)
// {
// trace (+i+'.\t name:' + pageHolder.getChildAt(i).name + '\t type:' + typeof (pageHolder.getChildAt(i)));
// }
trace ("up!");
}
Edit: clarified code a bit
Edit2: TL;DR: Problem is the last bit of code with the three functions that fail to control the timeline of mc page1, which is a child of mc pages, which is pushed frame by frame into an array and loaded at runtime into the sprite pageHolder. How to control the timeline of the single page movieclips?
Finally got it to work. This will work as a perfect powerpoint replacement :D
You will need to name the single slides (one movieclip on each frame of the mc pages) as page0, page1 etc. and then can control their timeline with this code (just replace the functions listed in the question above):
function f_nextFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame + 1);
}
function f_prevFrame():void
{
var temp:uint = pageHolder.numChildren - 1;
var frame:uint = ((pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].currentFrame);
(pageHolder.getChildAt(temp) as MovieClip)["page" + targetIDnew].gotoAndStop(frame - 1);
}

How can I prevent this arbitrary text truncation in AS3

Below is code that populates a menu. Everything seems to work great, with no errors thrown, except for one crucial part. My megaPages array has the values ["HOME","BABIES","BRIDALS","MISC","WEDDINGS","ABOUT"], but the actual text that displays on screen (which is produced by megaPages) is like this:
As you can see, some of the text is arbitrarily being truncated. I've traced the text strings as they get passed through the various functions at various stages of the menu-build, and they are always right, but somehow when each DisplayObject make it on screen, letters get ommitted (notice though that 'HOME' abd 'ABOUT' are fine). I don't even know where to start with this problem.
function buildMenu() {
var itemMCs = new Array();
for (var i = 0; i < megaPages.length; i++) {
megaPages[i] = megaPages[i].toUpperCase();
trace(megaPages[i]); // at each iteration, traces as follows "HOME","BABIES","BRIDALS","MISC","WEDDINGS","ABOUT"
var textMC = createText(megaPages[i]);
var itemMC = new MovieClip();
if (i!=0) {
var newLink = new PlateLink();
newLink.y = 0;
itemMC.addChild(newLink);
}
var newPlate = new Plate();
if (i==0) {
newPlate.y = 0;
} else {
newPlate.y = newLink.height - 2;
}
newPlate.x = 0;
newPlate.width = textMC.width + (plateMargin*2);
itemMC.addChild(newPlate);
if (i!=0) {
newLink.x = (newPlate.width/2) - (newLink.width/2);
}
textMC.x = plateMargin;
textMC.y = newPlate.y + .5;
itemMC.addChild(textMC);
itemMCs.push(itemMC);
itemMC.x = (homeplateref.x + (homeplateref.width/2)) - (itemMC.width/2);
if (i==0) {
itemMC.y = homeplateref.y;
} else {
itemMC.y = itemMCs[i-1].y + (itemMCs[i-1].height - 6);
}
menuRef.addChild(itemMC);
}
}
function createText(menuTitle) {
trace(menuTitle);
var textContainer : MovieClip = new MovieClip();
var myFont = new Font1();
var backText = instantText(menuTitle, 0x000000);
backText.x = 1;
backText.y = 1;
var frontText = instantText(menuTitle, 0xFFFFFF);
frontText.x = 0;
frontText.y = 0;
textContainer.addChild(backText);
textContainer.addChild(frontText);
return textContainer;
}
function instantText(textContent, color) {
trace(textContent); // again, traces the right text each time it is fired
var myFont = new Font1();
var myFormat:TextFormat = new TextFormat();
myFormat.size = 18;
myFormat.align = TextFormatAlign.CENTER;
myFormat.font = myFont.fontName;
var myText:TextField = new TextField();
myText.defaultTextFormat = myFormat;
myText.embedFonts = true;
myText.antiAliasType = AntiAliasType.ADVANCED;
myText.text = textContent;
myText.textColor = color;
myText.autoSize = TextFieldAutoSize.LEFT;
trace(myText.text);
return myText;
}
You need to embed all the necessary characters for the font you're using.
For textfields created in Flash:
Select the TextField, and hit the 'Embed' button in the properties panel.
For dynamically created textfields:
When you set the font to export (Font1 in your case) make sure to include all the characters you need.
You can choose to embed all uppercase characters, or just type in the ones you need for those specific menu items.

Resources