I'd like to enable layout animation for some components but it once it is activated, all components being rendered are affected by layout animation. For example, I have
<container>
<waterfall/>
<menu/>
</container>
I only wish to use layout animation for the component menu but the animation is applied to the waterfall rendering which already has its own animation code.
Is this possible with react native?
I ran into a similar problem, here's how I solved it with layoutanimation:
Explanation
Keep a state variable in the container component that is passed as a prop to menu: <menu reload={this.state.doSomethingInMenu}>
In the container component, use setTimeout to set the menu variable so control is passed back to the DOM and changes will render (without animation). After setTimeout is ran, the variable will update and the menu props will change, causing it to reload.
In the menu component, check to see if that variable has been updated to the value you're expecting. If it has, call LayoutAnimation.configureNext. This will cause the next rendering (just the menu changes) to be animated.
Code Overview
container component
getInitialState: function() {
return { doSomethingInMenu: false };
},
// Do something that causes the change
// Then call setTimeout to change the state variable
setTimeout(() => {
this.setState({ doSomethingInMenu: true });
},750)
<menu reload={this.state.doSomethingInMenu} />
menu component
componentWillReceiveProps: function(nextProps) {
if (nextProps.reload) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
// Set the menu state variable that causes the update
this.setState({field: value})
}
},
If the solution above doesn't work, try changing it from LayoutAnimation to Animated, this one allows you to have more control on the animations.
You can check react-native-reanimated Transitions https://github.com/kmagiera/react-native-reanimated#transitions-
It works similarly as LayoutAnimations but gives more control on how animations will perform
Related
I'm using React-Native-Navigation from Wix (version 2) to setup navigation in my React Native app. I'm using the sideMenu layout with the center section being a stack. When the user selects one of the side menu items the selected view is pushed onto that center stack. If the user presses their back button on Android, then the view is popped from the stack, but I don't always want this to happen, mainly if the view they selected is a WebView.
If the view is a WebView, I want to manually handle the user pressing the hardware back button. If the WebView can "goBack" then the view will go back, but if it can't then the view will be popped from the stack (as it normally would).
I've tried overriding the back button press using the BackHandler class from react-native and this allows me to capture that press and have the WebView go back if able, but the act of popping the view from the stack also fires. Is there a way in React-Native-Navigation v2 to tell it, "Hey I got this, don't pop unless I tell you to."?
My current code for this section is as follows:
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.backHandler);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.backHandler);
}
backHandler = () => {
if (this.state.canGoBack) {
this.webviewRef.current.goBack();
// I thought this might force the back press to be
// ignored by react-native-navigation, but no dice.
return false;
} else {
// WebView can't go back so pop view like normal
Navigation.pop(this.props.componentId);
}
}
I was expecting this to only pop the view from the stack if the WebView can't currently go back and otherwise just have the WebView go back.
What actually occurs is both events fire. I.e. the WebView goes back, but the view is also popped from the stack.
I was able to find the answer to this through some more digging in the React Native Navigation and React Native docs.
The event subscriptions are called in reverse order (i.e. last registered subscription first), and if one subscription returns true then subscriptions registered earlier will not be called.
So the issue was in my backHandler method. Instead of returning false I needed to return true.
backHandler = () => {
if (this.state.canGoBack) {
this.webviewRef.current.goBack();
// We've handled the event so we return true and the
// handler on the view's parent can effectively be ignored. Yay!
return true;
} else {
// WebView can't go back so pop view like normal
Navigation.pop(this.props.componentId);
}
}
I'm writing a Primefaces 5.1 portlet.
It consists in a unique page containing a panelMenu, and I need that it starts with any panel collapsed everytime a user change page (on page loading).
But, if I open a panel, then change page, it will start showing that panel still opened.
I wasn't able to find any option to achieve this goal (e.g. collapsed=true, ignoreCookie=true or something similar).
The only solution I found was the following Javascript code:
PrimeFaces.widgets.myPanelMenu.collapseRootSubmenu(PrimeFaces.widgets.myPanelMenu.headers);
The problem is that this code will collapse any opened panel (so on page loading user is able to see panel menu collapsing animation) but it seems it doesn't store this state in its cookie/localstorage... the result is that on any page loading user can see this animation.
I'm sure it doesn't save its state, because the only way to "solve" the problem is to manually re-open and re-collapse the panels... then, on following page change, these menus start closed (and there is no animation).
I also tried to use PrimeFaces.widgets.sideMenuPanel.saveState() after collapsing, but with no success.
Do you have any idea about?
Thank you...
I found a solution to the problem.
If you read my discussion with Kukeltje (comments on my question), you will find that latest Primefaces' versions will solve the problem.
Otherwise, if you want to avoid upgrade or modify sources, and you need a quick fix based on Javascript only please read the following part of the answer.
It directly works on the component's state using JavaScript.
First of all you need to have a variable reference to your component:
<p:panelMenu model="#{menuBackingBean.menuModel}" widgetVar="sidePanelMenu" />
Then you should add the following JS code on document ready:
var panelMenu = PrimeFaces.widgets.sidePanelMenu;
// 1. On page loading collapses possible opened panels
panelMenu.collapseRootSubmenu(panelMenu.headers);
// following line is commented because it never should be necessary is not necessary (unless unexpected situation I never verified)
//clearSidePanelMenuPreferences();
// 2. Call the "clear preferences" actions on click on two tpe of links: first level are the panel link (used to open/close the menu) and second level are the destination links
// We need to fork also on the first level links to be sure it works after user clicks there then exit from the page in another way
panelMenu.headers.children("a").click(function(){setTimeout(clearSidePanelMenuPreferences, 500)}); // setTimeout is necessary because this event should be fired after preferences are written
panelMenu.headers.siblings().find("a").click(function(){clearSidePanelMenuPreferences();});
The function called to clear preferences are the following:
function clearSidePanelMenuPreferences() {
var panelMenu = PrimeFaces.widgets.sidePanelMenu;
panelMenu.expandedNodes = []; // clear the opened panels lists
panelMenu.saveState(); // store this information
}
Hope it helps
Please check this block of code
PF('myPanelMenu').headers.each(
function(){
var header = jQuery(this);
PF('myPanelMenu').collapseRootSubmenu(header);
header.removeClass('ui-state-hover');
}
);
I prefer to do this in order to execute this method only once and keep the menu option selected.
$(document).ready(function() {
if(location.pathname == "/cotizador/" || location.pathname == "/cotizador/faces/login.xhtml"){
var panelMenu = PrimeFaces.widgets.sidePanelMenu;
// 1. On page loading collapses possible opened panels
panelMenu.collapseRootSubmenu(panelMenu.headers);
panelMenu.expandedNodes = []; // clear the opened panels lists
panelMenu.saveState();
}
});
I have a floating tree panel that shows (showBy) up below a combo box when I click on it. I would like a click outside the tree panel to hide it so I am trying a blur listener that is pasted below. But the blur listener is not called on click outside of the panel.
blur: function(tree, event, opts) {
treePanel.setVisible(false);
}
I also tried setting blur listener on tree panel's "el" but then on click of a tree node itself fires this listener. I don't completely understand why this happens.
el: {
blur: function() {
treePanel.setVisible(false);
}
}
Can someone suggest how I should approach this problem? Thanks.
You have to set the focus on your floating grid. blur will only work if the component looses focus. In order to loose the focus, it must first get the focus.
In your code replace treePanel.setVisible(true); with
treePanel.setVisible(true)
treePanel.focus()
How do I stop my Google Chrome extension's default action to auto-focus the first link in my popup.html? I know I could probably do some roundabout hack with JS or change the :focus CSS, but I think this is throwing off something else I'm trying to do and I'd prefer to stop the root cause of it.
The easiest (and javascript free!) way is to simply add tabindex="-1" to any element which you don't want to receive automatic focus.
Perhaps auto-focus was intended for a convenience, but often it does a disservice. Since I see no way to stop the root cause, I found some roundabouts. One is using JavaScript. Chrome puts auto-focus after a short delay after displaying the popup. It's possible to unfocus it with blur() but unfocusing it too late will flash it momentarily, and trying to unfocus too early will do nothing. So to find the right time to unfocus is not easy, and this solution tries to do this several times during the first second after the popup is displayed:
document.addEventListener("DOMContentLoaded", function () {
var blurTimerId = window.setInterval(function() {
if (document.activeElement != document.body) {
document.activeElement.blur();
}
}, 200);
window.setTimeout(function() {
window.clearInterval(blurTimerId);
}, 1000);
});
Another pure HTML solution is to add tabindex="1" to the body tag.
Wrangling the initially focused element with a tabindex attribute is probably the best way to go, using:
tabindex="-1", as suggested by Paul Ferret to prevent an element from getting focus
tabindex="1", as suggested by link0ff, to specify which element should start with focus
If your situation is more complicated and you do want to bring in some javascript, I'd recommend using link0ff's solution, except, instead of trying to guess when to blur with timeouts, listen for the initial focus in event:
function onInitialFocus(event) {
// Any custom behavior your want to perform when the initial element is focused.
// For example: If this is an anchor tag, remove focus
if (event.target.tagName == "A") {
event.target.blur();
}
// ALSO, remove this event listener after it is triggered,
// so it's not applied to subsequent focus events
document.removeEventListener("focusin", onInitialFocus);
}
// NOTE: the "focusin" event bubbles up to the document,
// but the "focus" event does not.
document.addEventListener("focusin", onInitialFocus);
I don't believe the focus event is cancelable, so you can't just suppress the event.
Another easy alternative (which preserves "tabbability") is to just add an empty link () before your first actual link. It will invisibly "catch" the auto-focus from Chrome, but any users who want to tab through the links will still be able to do so normally.
The only minor downside of this approach is that it introduces a second "dead tab" when looping; that is, users will have to press tab three times to get from the last link back to the first, instead of just twice.
tabindex="-1" worked for me. I was adding autofocus to an input and it didn't work until I used this tabindex="-1" attribute for each link before the input.
Strange to say the least.
This is the best solution to the problem. The tabindex="-1" solution harms user experience, and as opposed to #link0ff's solution, this one removes the focus instantly.
This element should be the first focusable element in the DOM:
<button class="invisible-button"></button>
This simply removes the button once it's been focused:
function removeAutoFocus(e) {
if (e.target.classList.contains("invisible-button")) {
e.target.style.display = "none";
document.removeEventListener("focus", removeAutoFocus);
}
}
document.addEventListener("focus", removeAutoFocus, true);
I used an ice:dataTable component to display data, somehow there was a default onscroll event in the target div which doesn't make any sense to me and it had brought some positioning problems to other float elements within the page. I want to disable this action, but I didn't find any approach to control this.
It called the function below:
var input = document.getElementById('targetId'); clearTimeout(ice.pid);
ice.pid = setTimeout(function() {
input.value =
document.getElementById('targetId_scroll').scrollTop;
window.iceSubmitPartial(null, input, event);
}, 400);
I had a similar problem with ICEFaces and as we upgraded the JSF and ICEFaces versions the best solution was to change the tags:
ice:dataTable tag to ace:dataTable and the columns inside from ice:column to ace:column.
But, if you didn't upgrade ICEFaces version, I guess you could use a JavaScript or jQuery in some point of your code removing the onscroll attribute, like:
document._getElementsByXPath("//div[contains(#onscroll,'scroll')]")[0].removeAttribute("onscroll")
This one isn't a good one, just a example, cause you can remove scroll from other tables.
jQuery('.myClass div[id$="_scroll"]')[0].removeAttribute("onscroll")