Why does overflow-x hidden has interaction with the client bounding rect - vue.js

I have this Vue directive to detect if I scroll past a certain element. But it stopped working since I set overflow-x to hidden. So I logged all the comparisons with the client bounding rect. How can I make my scroll detection work again?
import Vue from 'vue'
Vue.directive(
"infocus" , {
bind: function (el, binding, vnode) {
let f = () => {
let clientBoundingRect = el.getBoundingClientRect();
console.log(clientBoundingRect.width > 0);
console.log(clientBoundingRect.height > 0);
console.log(clientBoundingRect.top >= 0);
console.log(clientBoundingRect.width <= (window.innerHeight || document.documentElement.clientHeight));
let isInView = (
clientBoundingRect.width > 0 &&
clientBoundingRect.height > 0 &&
clientBoundingRect.top >= 0 &&
clientBoundingRect.bottom <= (window.innerHeight || document.documentElement.clientHeight)
)
if (isInView) {
if(typeof binding.value === "function")
binding.value(el);
else
throw new Error("v-infocus requires you to bind a method.");
window.removeEventListener('scroll', f)
}
}
window.addEventListener('scroll', f);
f();
}
}
)

Related

Array is not cloned, wrapped inside Proxy

Im executing this code inside vue created() method:
const width = window.innerWidth;
const columns = this.columns.slice()
let colVis = columns.map((element) => {
if(element.sClass == 'min-tv' && width < 2500) {
element.visible = false;
return element;
} else if(element.sClass == 'min-desktop-lg' && width < 1980) {
element.visible = false;
return element;
} else {
return element;
}
});
console.log(columns);
console.log(colVis);
For some reason both arrays returns same values (and this.columns too).
Everything is wraped inside Proxy - arrays and their values.
I cant understand whats going on and why i can't have clone of array?
I don't use computed, because it's initialization values (for Datatables colvis).
You should use filter to filter data from an array.
const width = window.innerWidth;
const columns = this.columns.slice()
let colVis = columns.map((element) => {
if(element.sClass == 'min-tv' && width < 2500) {
element.visible = false;
return element;
} else if(element.sClass == 'min-desktop-lg' && width < 1980) {
element.visible = false;
return element;
} else {
return element;
}
}).filter((element) => element.visible);
console.log(columns);
console.log(colVis);
It turns out to be vue bug.
Using inline window.innerWidth breaks its normal behavior.

React native run useState/force rerender inside worklet funtion

I'm calling the useAnimatedScrollHandler hook from react-native-reanimated to handle my onScroll function on an Animated.ScrollView. This hook works as expected but I now want to disable a custom button (My FlatButton) based on the currentIndex which is a sharedValue. But when the sharedValue changes the screen doesn't get rerendered, because the state doesn't change so the look of my button remains the same.
Is there a way to force a rerender inside of a worklet, or is it possible to use useState to force a rerender from inside a worklet?
const scrollHandler = useAnimatedScrollHandler((event) => {
translationX.value = event.contentOffset.x
if (event.contentOffset.x < width * 0.5 && currentIndex.value != 0) {
currentIndex.value = 0
} else if (
event.contentOffset.x > width * 0.5 &&
event.contentOffset.x < width * 1.5 &&
currentIndex.value != 1
) {
currentIndex.value = 1
} else if (event.contentOffset.x > width * 1.5 && currentIndex.value != 2) {
currentIndex.value = 2
}
})
<FlatButton
label="Next"
disabled={
(currentIndex.value == 0 && (!firstName || !lastName)) ||
(currentIndex.value == 1 && (!dateOfBirth || !sex)) ||
(currentIndex.value == 2 &&
(!streetNumber || !postalCode || !city || !state || !country))
}
onPress={() => {
if (currentIndex.value == 0) {
scrollRef.current
?.getNode()
.scrollTo({ x: width, animated: true })
} else if (currentIndex.value == 1) {
scrollRef.current?.getNode().scrollToEnd({ animated: true })
}
}}
/>
I just found out that reanimated offers the function runOnJS which makes it possible to run a javscript function like setState inside a worklet. So just create a wrapper function, like in my case toggleIndex in which you interact with your state and call this function inside runOnJS from your worklet.
const [currentIndex, setCurrentIndex] = useState(0)
const toggleIndex = (index: number) => {
setCurrentIndex(index)
}
const scrollHandler = useAnimatedScrollHandler((event) => {
translationX.value = event.contentOffset.x
if (event.contentOffset.x < width * 0.5 && currentIndex != 0) {
runOnJS(toggleIndex)(0)
} else if (
event.contentOffset.x > width * 0.5 &&
event.contentOffset.x < width * 1.5 &&
currentIndex != 1
) {
runOnJS(toggleIndex)(1)
} else if (event.contentOffset.x > width * 1.5 && currentIndex != 2) {
runOnJS(toggleIndex)(2)
}
})

ag grid vue grouping set columns expanded after component reload

I use ag-grid table - I am grouping the columns e.g. like:
Is it possible to set columns expanded the same way they were after components reload?
How to save how columns were expanded and then reload it?
One way is to store the ids of the nodes which are expanded (I do so in local storage as there aren't many rows in my table and I know I won't store anything confidential). Then on reload, retrieve the nodes that should be expanded and expand them:
<ag-grid-angular
(rowGroupOpened)="onRowGroupOpened()"
(gridReady)="onGridReady($event)">
</ag-grid-angular>
localStorageKey = 'storage-key-name';
onRowGroupOpened(): void {
let allExpanded = true;
const expandedNodeDetails: string[] = [];
if (this.myGrid.gridApi != null) {
this.myGrid.gridApi.forEachNode(node => {
if (node.group || (node.allChildrenCount > 0)) {
if (!this.restoringExpandedNodes) {
expandedNodeDetails.push(node.key);
}
}
});
}
if (!this.restoringExpandedNodes) {
localStorage.setItem(this.localStorageKey, JSON.stringify(expandedNodeDetails));
}
}
onGridReady(): void {
this.restoreExpandedNodes();
}
restoreExpandedNodes(): void {
const itemsInStorage = JSON.parse(localStorage.getItem(this.localStorageKey));
if ((itemsInStorage != null) && (this.myGrid != null) && (this.myGrid.gridApi != null)) {
this.restoringExpandedNodes = true;
this.myGrid.gridApi.forEachNode(node => {
if (node.group || (node.allChildrenCount > 0)) {
const expandedDetails = this.getExpandedDetails(node, null);
const index = itemsInStorage.findIndex(storageItem => storageItem === expandedDetails);
if (index !== -1) {
node.expanded = true;
} else if ((itemToSelect != null) && (node.key == itemToSelect.ItemFullName)) {
node.expanded = true;
}
}
});
this.myGrid.gridApi.onGroupExpandedOrCollapsed();
this.restoringExpandedNodes = false;
}
}
I've had to sanitise this code so please let me know if something doesn't make sense

How to make a flag counter with react-native-beacons-manager

Scenario
Our app aims to detect beacons placed inside the restaurants our app
uses react-native-beacons-manager
When our app detects a beacon, I have developed a cloud function that accepts the beacon's major key and use it to query data of that restaurant from my database
The Cloud function then sends a push notification on the user about the restaurant details.
The Problem
The way I detect the beacons is not stable. this is my flow. I created a function located at
this.beaconsDidRangeEvent = Beacons.BeaconsEventEmitter.addListener(
//function-here
);
I can receive the beacons information like uuid, major and minor key and proximity (immediate, near, far, unknown) . Now inside that function I use the major key to determine the individuality of each beacons. Now, I've made a condition like this:
let beaconArr = data.beacons;
console.log(beaconArr);
console.log(count);
if (beaconArr.length > 0) {
console.log("beacons detected!");
let major = data.beacons[0].major;
let prox = data.beacons[0].proximity;
if ((prox === "near" || prox === "far") && beaconFlag === false && count === 0) {
console.log("beacon Action");
this.props.beaconAction(major);
this.props.createCheckInHistory(user.uid);
beaconFlag = true;
count++;
} else {
console.log("counter turned to 1!");
console.log(data);
beaconFlag = true;
}
} else {
console.log("no beacons detected!");
count = 0;
beaconFlag = false;
}
Expected Result
I expect that the functions inside the condition is true will only fire once.
Actual Result
Sometimes, its ok sometimes its not. even if im still at the range of the beacon, suddenly the beacon's array got 0. Then suddenly i'll receive a push notification again and again.
componentDidMount() Code
componentDidMount() {
this.props.selectedIcon('map');
firebase
.messaging()
.getInitialNotification()
.then(notification => {
console.log("Notification which opened the app: ", notification);
});
const user = firebase.auth().currentUser;
let count = 0;
let beaconFlag = false;
// will be set as a reference to "regionDidEnter" event:
this.beaconsDidRangeEvent = Beacons.BeaconsEventEmitter.addListener(
"beaconsDidRange",
_.throttle(data => {
let beaconArr = data.beacons;
console.log(beaconArr);
console.log(count);
if (beaconArr.length > 0) {
console.log("beacons detected!");
let major = data.beacons[0].major;
let prox = data.beacons[0].proximity;
if ((prox === "near" || prox === "far") && beaconFlag === false && count === 0) {
console.log("beacon Action");
this.props.beaconAction(major);
this.props.createCheckInHistory(user.uid);
beaconFlag = true;
count++;
} else {
console.log("counter turned to 1!");
console.log(data);
beaconFlag = true;
}
} else {
console.log("no beacons detected!");
count = 0;
beaconFlag = false;
}
}, 3000)
);
// monitoring events
this.regionDidEnterEvent = Beacons.BeaconsEventEmitter.addListener(
"regionDidEnter",
data => {
console.log("monitoring - regionDidEnter data: ", data);
}
);
// Monitoring: Listen for device leaving the defined region
this.regionDidExitEvent = Beacons.BeaconsEventEmitter.addListener(
"regionDidExit",
data => {
console.log("monitoring - regionDidExit data: ", data);
}
);
}
This is a common problem when ranging in beacon apps. Sometimes the detected beacons will briefly drop out then come back again. This can be solved by a software filter where you keep track of all beacons you have recently seen, and only perform an operation of it has not happened recently. In your case, you may use the major as the key to the index into the filter object.
// scope this globally
var minimumRetriggerMillis = 3600 * 1000; // 1hr
var recentTriggers = {};
// Before executing your trigger action:
var now = new Date().getTime();
if (recentTriggers[minor] == null || now-recentTriggers[minor] > minimumRetriggerMillis) {
recentTriggers[minor] = now;
// TODO: execute trigger logic here
}

Disable carousel overscroll/overdrag in Sencha Touch

At the end or beginning of a Sencha Touch 2 carousel, a user can drag the item past where it should be able to go and display the white background (screenshot here: http://i.imgur.com/MkX0sam.png). I'm trying to disable this functionality, so a user can't drag past the end/beginning of a carousel.
I've attempted to do this with the various scrollable configurations, including the setup that is typically suggested for dealing with overscrolling
scrollable : {
direction: 'horizontal',
directionLock: true,
momentumEasing: {
momentum: {
acceleration: 30,
friction: 0.5
},
bounce: {
acceleration: 0.0001,
springTension: 0.9999,
},
minVelocity: 5
},
outOfBoundRestrictFactor: 0
}
The above configuration, especially outOfBoundRestrictFactor does stop the ability to drag past the end, but it also stops the ability to drag anywhere else in a carousel either...so that doesn't work. I've screwed around with all of the other configurations to no positive effect.
Unfortunately, I haven't been able to find much on modifying the configurations of dragging. Any help here would be awesomesauce.
What you need to do is override the onDrag functionality in Carousel. This is where the logic is to detect which direction the user is dragging, and where you can check if it is the first or last item.
Here is a class that does exactly what you want. The code you are interested in is right at the bottom of the function. The rest is simply taken from Ext.carousel.Carousel.
Ext.define('Ext.carousel.Custom', {
extend: 'Ext.carousel.Carousel',
onDrag: function(e) {
if (!this.isDragging) {
return;
}
var startOffset = this.dragStartOffset,
direction = this.getDirection(),
delta = direction === 'horizontal' ? e.deltaX : e.deltaY,
lastOffset = this.offset,
flickStartTime = this.flickStartTime,
dragDirection = this.dragDirection,
now = Ext.Date.now(),
currentActiveIndex = this.getActiveIndex(),
maxIndex = this.getMaxItemIndex(),
lastDragDirection = dragDirection,
offset;
if ((currentActiveIndex === 0 && delta > 0) || (currentActiveIndex === maxIndex && delta < 0)) {
delta *= 0.5;
}
offset = startOffset + delta;
if (offset > lastOffset) {
dragDirection = 1;
}
else if (offset < lastOffset) {
dragDirection = -1;
}
if (dragDirection !== lastDragDirection || (now - flickStartTime) > 300) {
this.flickStartOffset = lastOffset;
this.flickStartTime = now;
}
this.dragDirection = dragDirection;
// now that we have the dragDirection, we should use that to check if there
// is an item to drag to
if ((dragDirection == 1 && currentActiveIndex == 0) || (dragDirection == -1 && currentActiveIndex == maxIndex)) {
return;
}
this.setOffset(offset);
}
});