react-native-intersection-observer
Advanced tools
Comparing version 0.0.9 to 0.1.0
@@ -1,6 +0,7 @@ | ||
import IntersectionObserver from './IntersectionObserver'; | ||
import InView from './InView'; | ||
import IntersectionObserver, { type IntersectionObserverEntry, type IntersectionObserverOptions, type RootMargin } from './IntersectionObserver'; | ||
import InView, { type InViewProps } from './InView'; | ||
import IOContext from './IOContext'; | ||
import IOScrollView from './IOScrollView'; | ||
import withIO from './withIO'; | ||
import IOScrollView, { type IOScrollViewProps } from './IOScrollView'; | ||
import withIO, { type IOScrollableComponentProps } from './withIO'; | ||
export type { IntersectionObserverEntry, IntersectionObserverOptions, RootMargin, InViewProps, IOScrollViewProps, IOScrollableComponentProps, }; | ||
export { IntersectionObserver, InView, IOContext, IOScrollView, withIO }; |
@@ -9,68 +9,6 @@ import throttle from 'lodash/throttle'; | ||
class IntersectionObserver { | ||
callback; | ||
options; | ||
targets; | ||
constructor(callback, options) { | ||
this.measureTarget = (target) => { | ||
const rootNode = this.options.root.node; | ||
if (rootNode) { | ||
target.measureLayout(rootNode, (x, y, width, height) => { | ||
target.layout = { | ||
x, | ||
y, | ||
width, | ||
height, | ||
}; | ||
this.handleScroll(); | ||
}); | ||
} | ||
}; | ||
this.handleLayout = throttle(() => { | ||
for (let index = 0; index < this.targets.length; index += 1) { | ||
this.measureTarget(this.targets[index]); | ||
} | ||
}, 300, { leading: false, trailing: true }); | ||
this.handleScroll = throttle(() => { | ||
const rootMargin = this.options?.rootMargin || defaultRootMargin; | ||
const { horizontal, current: { contentOffset, contentSize, layoutMeasurement, }, } = this.options.root; | ||
if (contentSize.width <= 0 || | ||
contentSize.height <= 0 || | ||
layoutMeasurement.width <= 0 || | ||
layoutMeasurement.height <= 0) { | ||
return; | ||
} | ||
const contentOffsetWithLayout = horizontal | ||
? contentOffset.x + layoutMeasurement.width | ||
: contentOffset.y + layoutMeasurement.height; | ||
const changedTargets = []; | ||
for (let index = 0; index < this.targets.length; index += 1) { | ||
const target = this.targets[index]; | ||
const targetLayout = target.layout; | ||
if (!targetLayout || | ||
targetLayout.width === 0 || | ||
targetLayout.height === 0) { | ||
continue; | ||
} | ||
let isIntersecting = false; | ||
if (horizontal) { | ||
isIntersecting = | ||
contentOffsetWithLayout + (rootMargin.right || 0) >= | ||
targetLayout.x && | ||
contentOffset.x - (rootMargin.left || 0) <= | ||
targetLayout.x + targetLayout.width; | ||
} | ||
else { | ||
isIntersecting = | ||
contentOffsetWithLayout + (rootMargin.bottom || 0) >= | ||
targetLayout.y && | ||
contentOffset.y - (rootMargin.top || 0) <= | ||
targetLayout.y + targetLayout.height; | ||
} | ||
if (target.inView !== isIntersecting) { | ||
target.inView = isIntersecting; | ||
changedTargets.push({ | ||
target, | ||
isIntersecting, | ||
}); | ||
} | ||
} | ||
this.callback(changedTargets); | ||
}, 100, { leading: false, trailing: true }); | ||
this.callback = callback; | ||
@@ -82,2 +20,67 @@ this.options = options; | ||
} | ||
measureTarget = (target) => { | ||
const rootNode = this.options.root.node; | ||
if (rootNode) { | ||
target.measureLayout(rootNode, (x, y, width, height) => { | ||
target.layout = { | ||
x, | ||
y, | ||
width, | ||
height, | ||
}; | ||
this.handleScroll(); | ||
}); | ||
} | ||
}; | ||
handleLayout = throttle(() => { | ||
for (let index = 0; index < this.targets.length; index += 1) { | ||
this.measureTarget(this.targets[index]); | ||
} | ||
}, 300, { leading: false, trailing: true }); | ||
handleScroll = throttle(() => { | ||
const rootMargin = this.options?.rootMargin || defaultRootMargin; | ||
const { horizontal, current: { contentOffset, contentSize, layoutMeasurement, }, } = this.options.root; | ||
if (contentSize.width <= 0 || | ||
contentSize.height <= 0 || | ||
layoutMeasurement.width <= 0 || | ||
layoutMeasurement.height <= 0) { | ||
return; | ||
} | ||
const contentOffsetWithLayout = horizontal | ||
? contentOffset.x + layoutMeasurement.width | ||
: contentOffset.y + layoutMeasurement.height; | ||
const changedTargets = []; | ||
for (let index = 0; index < this.targets.length; index += 1) { | ||
const target = this.targets[index]; | ||
const targetLayout = target.layout; | ||
if (!targetLayout || | ||
targetLayout.width === 0 || | ||
targetLayout.height === 0) { | ||
continue; | ||
} | ||
let isIntersecting = false; | ||
if (horizontal) { | ||
isIntersecting = | ||
contentOffsetWithLayout + (rootMargin.right || 0) >= | ||
targetLayout.x && | ||
contentOffset.x - (rootMargin.left || 0) <= | ||
targetLayout.x + targetLayout.width; | ||
} | ||
else { | ||
isIntersecting = | ||
contentOffsetWithLayout + (rootMargin.bottom || 0) >= | ||
targetLayout.y && | ||
contentOffset.y - (rootMargin.top || 0) <= | ||
targetLayout.y + targetLayout.height; | ||
} | ||
if (target.inView !== isIntersecting) { | ||
target.inView = isIntersecting; | ||
changedTargets.push({ | ||
target, | ||
isIntersecting, | ||
}); | ||
} | ||
} | ||
this.callback(changedTargets); | ||
}, 100, { leading: false, trailing: true }); | ||
observe(target) { | ||
@@ -84,0 +87,0 @@ const index = this.targets.indexOf(target); |
@@ -5,49 +5,14 @@ import React, { PureComponent, } from 'react'; | ||
class InView extends PureComponent { | ||
static contextType = IOContext; | ||
static defaultProps = { | ||
triggerOnce: false, | ||
as: View, | ||
}; | ||
context = undefined; | ||
mounted = false; | ||
element; | ||
instance; | ||
view; | ||
constructor(props) { | ||
super(props); | ||
this.mounted = false; | ||
this.handleChange = (inView) => { | ||
if (this.mounted) { | ||
const { triggerOnce, onChange } = this.props; | ||
if (inView && triggerOnce) { | ||
if (this.context?.manager) { | ||
this.context?.manager.unobserve(this.element); | ||
} | ||
} | ||
if (onChange) { | ||
onChange(inView); | ||
} | ||
} | ||
}; | ||
this.handleRef = (ref) => { | ||
this.view = ref; | ||
}; | ||
this.handleLayout = (event) => { | ||
const { nativeEvent: { layout }, } = event; | ||
if (layout.width !== this.element.layout.width || | ||
layout.height !== this.element.layout.height) { | ||
if (this.element.onLayout) { | ||
this.element.onLayout(); | ||
} | ||
} | ||
const { onLayout } = this.props; | ||
if (onLayout) { | ||
onLayout(event); | ||
} | ||
}; | ||
this.measureInWindow = (...args) => { | ||
this.view.measureInWindow(...args); | ||
}; | ||
this.measureLayout = (...args) => { | ||
this.view.measureLayout(...args); | ||
}; | ||
this.setNativeProps = (...args) => { | ||
this.view.setNativeProps(...args); | ||
}; | ||
this.focus = (...args) => { | ||
this.view.focus(...args); | ||
}; | ||
this.blur = (...args) => { | ||
this.view.blur(...args); | ||
}; | ||
this.element = { | ||
@@ -76,2 +41,46 @@ inView: false, | ||
} | ||
handleChange = (inView) => { | ||
if (this.mounted) { | ||
const { triggerOnce, onChange } = this.props; | ||
if (inView && triggerOnce) { | ||
if (this.context?.manager) { | ||
this.context?.manager.unobserve(this.element); | ||
} | ||
} | ||
if (onChange) { | ||
onChange(inView); | ||
} | ||
} | ||
}; | ||
handleRef = (ref) => { | ||
this.view = ref; | ||
}; | ||
handleLayout = (event) => { | ||
const { nativeEvent: { layout }, } = event; | ||
if (layout.width !== this.element.layout.width || | ||
layout.height !== this.element.layout.height) { | ||
if (this.element.onLayout) { | ||
this.element.onLayout(); | ||
} | ||
} | ||
const { onLayout } = this.props; | ||
if (onLayout) { | ||
onLayout(event); | ||
} | ||
}; | ||
measureInWindow = (...args) => { | ||
this.view.measureInWindow(...args); | ||
}; | ||
measureLayout = (...args) => { | ||
this.view.measureLayout(...args); | ||
}; | ||
setNativeProps = (...args) => { | ||
this.view.setNativeProps(...args); | ||
}; | ||
focus = (...args) => { | ||
this.view.focus(...args); | ||
}; | ||
blur = (...args) => { | ||
this.view.blur(...args); | ||
}; | ||
render() { | ||
@@ -83,10 +92,5 @@ const { as, children, ...props } = this.props; | ||
const ViewComponent = (as || View); | ||
return (React.createElement(ViewComponent, Object.assign({}, props, { ref: this.handleRef, onLayout: this.handleLayout }), children)); | ||
return (React.createElement(ViewComponent, { ...props, ref: this.handleRef, onLayout: this.handleLayout }, children)); | ||
} | ||
} | ||
InView.contextType = IOContext; | ||
InView.defaultProps = { | ||
triggerOnce: false, | ||
as: View, | ||
}; | ||
export default InView; |
import IntersectionObserver from './IntersectionObserver'; | ||
class IOManager { | ||
io; | ||
observerId; | ||
instanceMap = new Map(); | ||
constructor(options) { | ||
this.instanceMap = new Map(); | ||
this.handleChange = (entries) => { | ||
for (let index = 0; index < entries.length; index += 1) { | ||
const { target, isIntersecting } = entries[index]; | ||
const instance = this.instanceMap.get(target); | ||
if (instance) { | ||
instance.callback(isIntersecting); | ||
} | ||
} | ||
}; | ||
this.io = new IntersectionObserver(this.handleChange, options); | ||
this.observerId = 0; | ||
} | ||
handleChange = (entries) => { | ||
for (let index = 0; index < entries.length; index += 1) { | ||
const { target, isIntersecting } = entries[index]; | ||
const instance = this.instanceMap.get(target); | ||
if (instance) { | ||
instance.callback(isIntersecting); | ||
} | ||
} | ||
}; | ||
observe(element, callback) { | ||
@@ -18,0 +20,0 @@ const existInstance = this.instanceMap.get(element); |
@@ -1,3 +0,4 @@ | ||
import { IOScrollView } from './withIO'; | ||
declare const _default: typeof IOScrollView; | ||
import { type IOScrollableComponent, type IOScrollableComponentProps } from './withIO'; | ||
export declare type IOScrollViewProps = IOScrollableComponentProps; | ||
declare const _default: typeof IOScrollableComponent; | ||
export default _default; |
import { PureComponent } from 'react'; | ||
import { ScrollView, ScrollViewComponent, ScrollViewProps } from 'react-native'; | ||
import { ScrollResponderMixin, ScrollView, ScrollViewComponent, ScrollViewProps } from 'react-native'; | ||
import { RootMargin } from './IntersectionObserver'; | ||
interface IOScrollViewProps extends ScrollViewProps { | ||
export interface IOScrollableComponentProps extends ScrollViewProps { | ||
rootMargin?: RootMargin; | ||
} | ||
export declare class IOScrollViewComponent extends PureComponent<IOScrollViewProps> { | ||
} | ||
export declare class IOScrollView extends IOScrollViewComponent { | ||
export declare class IOScrollableComponent extends PureComponent<IOScrollableComponentProps> { | ||
scrollTo: ScrollView['scrollTo']; | ||
scrollToEnd: ScrollView['scrollToEnd']; | ||
getScrollResponder: ScrollView['getScrollResponder']; | ||
getScrollResponder(): ScrollResponderMixin | undefined; | ||
getScrollableNode: ScrollView['getScrollableNode']; | ||
getInnerViewNode: ScrollView['getInnerViewNode']; | ||
} | ||
declare const withIO: (ScrollableComponent: typeof ScrollViewComponent) => typeof IOScrollView; | ||
declare const withIO: (ScrollableComponent: typeof ScrollViewComponent) => typeof IOScrollableComponent; | ||
export default withIO; |
@@ -6,52 +6,10 @@ import React, { PureComponent, createRef } from 'react'; | ||
const withIO = (ScrollableComponent) => { | ||
class IOScrollableComponent extends PureComponent { | ||
class IOScrollView extends PureComponent { | ||
node; | ||
scroller; | ||
root; | ||
manager; | ||
contextValue; | ||
constructor(props) { | ||
super(props); | ||
this.handleContentSizeChange = (width, height) => { | ||
const { contentSize } = this.root.current; | ||
if (width !== contentSize.width || height !== contentSize.height) { | ||
this.root.current.contentSize = { width, height }; | ||
if (width > 0 && height > 0 && this.root.onLayout) { | ||
this.root.onLayout(); | ||
} | ||
} | ||
const { onContentSizeChange } = this.props; | ||
if (onContentSizeChange) { | ||
onContentSizeChange(width, height); | ||
} | ||
}; | ||
this.handleLayout = (event) => { | ||
const { nativeEvent: { layout }, } = event; | ||
const { layoutMeasurement } = this.root.current; | ||
if (layoutMeasurement.width !== layout.width || | ||
layoutMeasurement.height !== layout.height) { | ||
this.root.current.layoutMeasurement = layout; | ||
} | ||
const { onLayout } = this.props; | ||
if (onLayout) { | ||
onLayout(event); | ||
} | ||
}; | ||
this.handleScroll = (event) => { | ||
this.root.current = event.nativeEvent; | ||
if (this.root.onScroll) { | ||
this.root.onScroll(this.root.current); | ||
} | ||
const { onScroll } = this.props; | ||
if (onScroll) { | ||
onScroll(event); | ||
} | ||
}; | ||
this.scrollToEnd = (options) => { | ||
this.scroller.current?.scrollToEnd(options); | ||
}; | ||
this.getScrollResponder = () => { | ||
return this.scroller.current?.getScrollResponder(); | ||
}; | ||
this.getScrollableNode = () => { | ||
return this.scroller.current?.getScrollableNode(); | ||
}; | ||
this.getInnerViewNode = () => { | ||
return this.scroller.current?.getInnerViewNode(); | ||
}; | ||
const self = this; | ||
@@ -102,12 +60,59 @@ this.scroller = createRef(); | ||
} | ||
handleContentSizeChange = (width, height) => { | ||
const { contentSize } = this.root.current; | ||
if (width !== contentSize.width || height !== contentSize.height) { | ||
this.root.current.contentSize = { width, height }; | ||
if (width > 0 && height > 0 && this.root.onLayout) { | ||
this.root.onLayout(); | ||
} | ||
} | ||
const { onContentSizeChange } = this.props; | ||
if (onContentSizeChange) { | ||
onContentSizeChange(width, height); | ||
} | ||
}; | ||
handleLayout = (event) => { | ||
const { nativeEvent: { layout }, } = event; | ||
const { layoutMeasurement } = this.root.current; | ||
if (layoutMeasurement.width !== layout.width || | ||
layoutMeasurement.height !== layout.height) { | ||
this.root.current.layoutMeasurement = layout; | ||
} | ||
const { onLayout } = this.props; | ||
if (onLayout) { | ||
onLayout(event); | ||
} | ||
}; | ||
handleScroll = (event) => { | ||
this.root.current = event.nativeEvent; | ||
if (this.root.onScroll) { | ||
this.root.onScroll(this.root.current); | ||
} | ||
const { onScroll } = this.props; | ||
if (onScroll) { | ||
onScroll(event); | ||
} | ||
}; | ||
scrollTo(y, x, animated) { | ||
this.scroller.current?.scrollTo(y, x, animated); | ||
} | ||
scrollToEnd = (options) => { | ||
this.scroller.current?.scrollToEnd(options); | ||
}; | ||
getScrollResponder = () => { | ||
return this.scroller.current?.getScrollResponder(); | ||
}; | ||
getScrollableNode = () => { | ||
return this.scroller.current?.getScrollableNode(); | ||
}; | ||
getInnerViewNode = () => { | ||
return this.scroller.current?.getInnerViewNode(); | ||
}; | ||
render() { | ||
return (React.createElement(IOContext.Provider, { value: this.contextValue }, | ||
React.createElement(ScrollableComponent, Object.assign({ scrollEventThrottle: 16 }, this.props, { ref: this.scroller, onContentSizeChange: this.handleContentSizeChange, onLayout: this.handleLayout, onScroll: this.handleScroll })))); | ||
React.createElement(ScrollableComponent, { scrollEventThrottle: 16, ...this.props, ref: this.scroller, onContentSizeChange: this.handleContentSizeChange, onLayout: this.handleLayout, onScroll: this.handleScroll }))); | ||
} | ||
} | ||
return IOScrollableComponent; | ||
return IOScrollView; | ||
}; | ||
export default withIO; |
{ | ||
"name": "react-native-intersection-observer", | ||
"version": "0.0.9", | ||
"version": "0.1.0", | ||
"description": "react native intersection observer", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
503
22613