How to specify custom transitions for specific routes, not screens (v5) - react-navigation

Question regarding react-navigation v5.
In previous versions, we were able to specify custom transition for specific routes, not screens, by doing the following inside a StackNavigator:
transitionConfig: () => ({
screenInterpolator: sceneProps => {
const {scenes, scene} = sceneProps;
const prevRoute = scenes[0].route.routeName === 'Route A';
// If prev route is A, and then current route is B, then we do a specific transition
if (prevRoute && scene.route.routeName === 'Route B') {
return StackViewStyleInterpolator.forVertical(sceneProps);
}
// Otherwise default to normal transition
return StackViewStyleInterpolator.forHorizontal(sceneProps);
},
}),
Now, I'm trying to achieve the same for react-navigation v5. I know I'm able to specify a custom animation per screen by doing something like:
<Stack.Screen name="Route B" component={RouteB} options={{ cardStyleInterpolator: CardStyleInterpolators.forVerticalIOS }} />
The problem is that I don't want this transition applied every time it is navigated to RouteB, ONLY when the previous route is RouteA, I want this transition applied, just like the previous code block above.
Couldn't find any example in the docs so would appreciate some help in migrating the code over to v5.

Something like this should work:
options={({ navigation, route }) => {
const state = navigation.dangerouslyGetState();
const index = state.routes.indexOf(route);
const previousRoute = state.routes[index - 1];
if (previousRoute?.name === 'RouteA') {
return {
cardStyleInterpolator: CardStyleInterpolators.forVerticalIOS,
gestureDirection: 'vertical',
};
} else {
return {};
}
}}

Related

How to check if element is fully uncovered

This is NOT the common cypress issue where you get a test failure because a fixed element is covered by another element.
I have an expandable list toward the top of my page. When it expands, I want it to be on top of every other aspect of the page. So I'm writing a cypress test to verify that nothing else is covering it.
Unfortunately, the test isn't failing in a clear failure case.
This test is succeeding for the above list
cy.get('#list')
.should('be.visible')
.find('p').each(($listItem) => {
cy.wrap($listItem)
.should('be.visible')
.click(); // another layer of cover check
});
I imagine this is succeeding because the elements aren't hidden and thus are 'visible,' and the click is succeeding because the center of each element is uncovered. How can I test that the list body is fully uncovered/displaying on top?
In the image the list is right-ish of the dropdown, so this is one way to check for overlap in the x-dimension:
cy.get('#dropdown').then($el => {
const rhs = Math.round($el[0].getBoundingClientRect().right)
cy.get('#list').then($el => {
const lhs = Math.round($el[0].getBoundingClientRect().left)
expect(lhs).to.be.gt(rhs)
})
})
It looks like there's also a table on the page which you'd want to repeat the check for.
To generalize a little bit:
Cypress.Commands.add('hasNoOverlapWith', {prevSubject: true}, (subject, others) => {
let covered = false;
const targetRect = subject[0].getBoundingClientRect()
others.forEach(other => {
cy.get(other).then($el => {
const otherRect = $el[0].getBoundingClientRect()
// other covers from the left
const coveredLeft = otherRect.right >= targetRect.left &&
otherRect.right <= targetRect.right
// other covers from the right
const coveredRight = otherRect.left <= targetRect.right &&
otherRect.left >= targetRect.left
if (!covered) {
covered = coveredLeft || coveredRight
}
})
})
cy.then(() => {
expect(covered).to.eq(false)
})
})
cy.get('#list').hasNoOverlapWith(['#dropdown', '#table'])

How to Perform Inplace Animations in React Three Fiber

I imported a 3D model from Mixamo with some animations and was wondering how to perform does animations in place?
The GLTF file I created from npx gltfjsx:
const { nodes, materials, animations } = useGLTF("/Mannequin.glb");
const { actions } = useAnimations(animations, heroRef);
return (
<>
<group ref={heroRef} dispose={null}>
<group rotation={[Math.PI / 2, 0, 0]} scale={0.01}>
<primitive object={nodes.mixamorig1Hips} />
<skinnedMesh
geometry={nodes.Ch36.geometry}
material={materials.Ch36_Body}
skeleton={nodes.Ch36.skeleton}
/>
</group>
{/* <gridHelper args={[25, 25]}/> */}
</group>
</>
);
}
useGLTF.preload("/Mannequin.glb");
For example, I would like the model to perform the run animation without changing locations (running on the spot) so that if I can perform user controls that translate the model and with the animation looping, it would look like they were actually running.
Are there any ways to do this? I have searched and could not find anything.
useEffect(() => {
console.log(actions) // find out the name of your action
actions.someAction.play()
});
This will make the animation play in place, if it was exported like that from mixamo, there is a setting.
Then you can put a around your mesh and make it move based on your control
I couldn't use console.log(actions) to see available actions as they were undefined and I had {} in console. My code was:
const { actions } = useAnimations(animations, group);
useEffect(() => {
console.log(actions);
}, [actions]);
So I digged inside into useAnimations source code and saw two things:
actions object is not being updated, only it's properties, so useEffect doesn't trigger when ref updates.
if ref.current is undefined (as for first render) properties are undefined. that's why I saw {} in console.
So I changed my code to:
const { actions } = useAnimations(animations, group);
useEffect(() => {
console.log(Object.keys(actions));
}, [actions]);
That's how I saw that my animation names were: ['Female_Idle', 'Female_Talk']
And
const { actions } = useAnimations(animations, group);
const action = actions['Female_Idle'];
useEffect(() => {
if (action) action.play();
}, [action]);
In order to play action, when ref updates. That did the trick.

ag grid Passing KeyPress/Enter event to apply filter

I want to use the same logic when I press the APPLY button but with the ENTER key.
How do I do that?
filterParams: {
closeOnApply:true,
buttons: ['reset', 'apply'],
values: parentCampaignAndNodes.PaymentGroups
},
As you might have found out by now, you can remove the Apply button (by setting filterParams: { buttons: [] } in your column definitions) and then values are submitted onchange.
The solution you ask for is indeed still not available through the AgGrid API. I do have a workaround, but beware it uses direct DOM bindings which is not recommended when working with React.
const applyFilterOnEnter: AgGridReactProps['onFilterOpened'] = ev => {
const inputElem = ev.eGui.querySelector('.ag-filter-body .ag-input-field');
const applyButtonElem = ev.eGui.querySelector('.ag-filter-apply-panel button[ref=applyFilterButton]');
if (inputElem && applyButtonElem) {
inputElem.addEventListener('keydown', keyEv => {
if ((keyEv as KeyboardEvent).key === 'Enter') {
(applyButtonElem as HTMLButtonElement).click();
}
});
}
};
return <AgGridReact
onFilterOpened={applyFilterOnEnter}
/>

Conditional class binding using computed prop Vue 2

I wanted to give some form visual validation cues, so I tried using class binding to do just that. If I use the ternary inline, it doesn't really meet my requirements of what should happen, but when I tried using computed prop, it made all the other components disappear.
If i tried doing it like this: v-bind:class="[(!validation.hasError('form.fullName'))?'has-success':'has-danger noValid']"
It just triggers the animation and the classes once and they stays there. I want to trigger the noValid animation everytime the user clicks my submit button if there's errors in validation.
I'm using simple-vue-validator btw.
Here's the godforsaken component, I use vue now-ui-kit template from Creative Tim as a base and customize my way from there. This is their Form Group Input component, docs here
<fg-input
class="input-lg"
:label="validation.firstError('form.fullName')"
placeholder="Full Name..."
v-model="form.fullName"
addon-left-icon="now-ui-icons users_circle-08"
v-bind:class="{ visualValidation }"
></fg-input>
Button Component: bootstrap-vue, cause their customized button doesn't really serve my purpose
<b-button type="submit" block pill variant="info" #click="submit">Submit Form</b-button>
My computation: noValid is the shaking animation class, has-success and has-danger just changes their appearances.
computed: {
visualValidation: function() {
const successClass = "has-success";
const errorBoi = "has-danger";
const shakeBoi = "noValid";
if (validation.firstError("form.fullName")) {
return errorBoi + " " + shakeBoi;
} else if (!validation.hasError("form.fullName")) {
return successClass;
}
}
}
I thought the variables that i returned would be binded as classes to the form.fullName Model but it's not doing anything, better yet, it made all my other components not rendering.
What should i do here? i'm just starting to learn Vue.js so i don't really understand what's going on here.
Edit from here:
I rewrote the logic to my class binding, and just use method to remove the class on click the components.
here is the updated component:
<fg-input
class="input-lg"
:label="validation.firstError('form.email')"
placeholder="Email Here..."
v-model="form.email"
addon-left-icon="now-ui-icons ui-1_email-85"
v-bind:class=" {'has-success' : isSuccEmail, 'has-danger' : isFailEmail, 'noValid': validNoEmail} "
#click="removeShake()"
></fg-input>
data:
isSuccEmail: false,
isFailEmail: false,
validNoEmail: false
method:
removeShake: function() {
let vm = this;
vm.validNoName = false;
vm.validNoEmail = false;
console.log(vm.validNoEmail);
console.log(vm.validNoName);
},
However, there's currently a bug, where the validator don't validate separately. It gave the class has-success to email even though it was full-name that was successful.
valEmail: function() {
let vm = this;
vm.$validate("form.email").then(function(success) {
if (success) {
vm.isFailEmail = false;
vm.isSuccEmail = true;
} else if (!success) {
vm.isSuccEmail = false;
vm.isFailEmail = true;
vm.validNoEmail = true;
} else {
alert("Fatal Error");
}
});
},
valName: function() {
let vm = this;
vm.$validate("form.fullName").then(function(success) {
if (success) {
vm.isFailName = false;
vm.isSuccName = true;
} else if (!success) {
vm.isSuccName = false;
vm.isFailName = true;
vm.validNoName = true;
console.log(vm);
} else {
alert("Fatal Error");
}
});
}
The $validate is a function of simple-vue-validator, the docs.
Submit function is just calling those two functions above.
I think this because of the promise call, is there a way to call the $validate() without promise?
There are a few problems here.
Firstly, while templates don't require you to use this. you still need to use it within your JS code. You should be seeing an error in the console, or maybe even at compile time depending on how you have linting configured.
if (validation.firstError("form.fullName")) {
What is validation? I assume that should be this.validation. Likewise a couple of lines further down.
Your next problem is here:
v-bind:class="{ visualValidation }"
The braces here are creating an object literal, so it's equivalent to this:
v-bind:class="{ visualValidation: visualValidation }"
This will be conditionally adding the string 'visualValidation' as a class , which isn't what you want. Get rid of the braces:
v-bind:class="visualValidation"

Force react-select to show all options using Elasticsearch

I'm trying to implement this with elasticsearch and it is working But how i can force this show results which can be different from the searched terms? For example i search ardino, The elasticsearch give me the word arduino but then react-select does not show that result because ardino does not contain arduino. I know the idea of this library is exactly that and it is working ok, but i have most of the things already implemented and it is only missing that part.
The handle is giving the right behavior and it is populating the options correctly.
Thank you
<Select
name= {this.state.selectedOption}
value={this.state.selectedOption}
isSearchable={true}
onChange = {(val) => {
this._handleSearch(val.value)
}}
onInputChange = {(val) => {
this._handleSearch(val)
}}
options={this.state.options}
/>
I would recommend using the Async component which can simplify your code. You will not need the onChange handler anymore.
isSearcheable is true by default, no need to specify.
In answer to your question: Make sure you are passing in label and value keys with each result option. If you want to customize any of the results, for example adding the search term in the results, you can manually manipulate the options array.
import React, {Component} from 'react';
import AsyncSelect from 'react-select/lib/Async';
class Search extends Component {
state = {inputValue: ""}
onInputChange = (inputValue) => {
this.setState({ inputValue });
};
getSearchResults = async (inputValue) => {
// elastic search results
let options = await fetch(`searchElastic/${inputValue}`);
// input value of drop-down
const inputValue = this.state.inputValue;
// manually add input field as an option in drop-down
if (inputValue.length > 0)
options.unshift({label: inputValue, value: inputValue})
// async handling of new props
return options.map(opt => {
return {label: opt.name, value: opt.value}
})
}
render() {
return <AsyncSelect
onInputChange={this.onInputChange}
loadOptions={this.getSearchResults}
/>
}
}
export default Search;

Resources