react-native-wishlist
Version:
The fastest List component for React Native.
244 lines (238 loc) • 7.99 kB
JavaScript
import React, { createContext, useContext, useImperativeHandle, useMemo, useRef } from 'react';
import { StyleSheet, useWindowDimensions, View } from 'react-native';
import { ForEach } from './Components/ForEach';
import { IF } from './Components/IF';
import { Pressable } from './Components/Pressable';
import { Case, Switch } from './Components/Switch';
import { WishlistImage } from './Components/WishlistImage';
import { WishlistText } from './Components/WishlistText';
import { WishlistView } from './Components/WishlistView';
import { initEventHandler } from './EventHandler';
import InflatorRepository from './InflatorRepository';
import NativeTemplateContainer from './NativeViews/NativeTemplateContainer';
import NativeTemplateInterceptor from './NativeViews/NativeTemplateInterceptor';
import NativeWishList, { Commands as WishlistCommands } from './NativeViews/WishlistNativeComponent';
import { TemplateContext } from './TemplateContext';
import { generateId } from './Utils';
import { useWishlistContext, WishlistContext } from './WishlistContext';
import { useInternalWishlistData } from './WishlistData';
import { createRunInJsFn, createRunInWishlistFn } from './WishlistJsRuntime';
const OffsetComponent = '__offsetComponent';
const TemplatesRegistryContext = /*#__PURE__*/createContext(null);
function getTemplatesFromChildren(children, width) {
const nextTemplates = {
[OffsetComponent]: /*#__PURE__*/React.createElement(View, {
style: [styles.offsetView, {
width
}]
})
};
React.Children.forEach(children, c => {
if (c.type.displayName === 'WishListTemplate') {
const templateElement = c;
nextTemplates[templateElement.props.type] = templateElement;
}
});
return nextTemplates;
}
function ComponentBase(_ref, ref) {
let {
children,
style,
initialData,
...rest
} = _ref;
const nativeWishlist = useRef(null); // TODO type it properly
const wishlistId = useRef(null);
if (!wishlistId.current) {
wishlistId.current = generateId();
}
const data = useInternalWishlistData(wishlistId.current, initialData);
useImperativeHandle(ref, () => ({
scrollToItem: (index, animated) => {
if (nativeWishlist.current != null) {
console.log('scrollTo', index);
WishlistCommands.scrollToItem(nativeWishlist.current, index, animated ?? true);
}
},
scrollToTop: () => {
if (nativeWishlist.current != null) {
WishlistCommands.scrollToItem(nativeWishlist.current, 0, true);
}
},
update: async updateJob => {
return new Promise((resolve, _reject) => {
const resolveJs = createRunInJsFn(resolve);
createRunInWishlistFn(() => {
'worklet';
// we have to do sth here to get rid of frozen objs
// otherwise data can't be modified
data().update(updateJob, resolveJs);
})();
});
}
}));
const {
width
} = useWindowDimensions();
useMemo(() => initEventHandler(), []);
// Template registration and tracking
const childrenTemplates = useMemo(() => getTemplatesFromChildren(children, width), [children, width]);
const templatesRegistry = useMemo(() => ({
templates: {},
registerTemplate(type, component) {
if (this.templates[type]) {
return;
}
this.templates[type] = component;
}
}), []);
// Resolve inflator - either use the provided callback or use the mapping
const resolvedInflater = useMemo(() => {
return (index, pool) => {
'worklet';
const value = data().at(index);
if (!value) {
return undefined;
}
console.log('returned', value, 'for index', index, 'data len', data().length());
const item = pool.getComponent(value.type);
if (!item) {
return undefined;
}
if (value.key == null) {
throw new Error('Every data cell has to contain unique key prop!');
}
// We set the key of the item here so that
// viewportObserver knows what's the key and is able to rerender it later on
item.key = value.key;
return [item, value];
};
}, [data]);
const inflatorIdRef = useRef(null);
const prevInflatorRef = useRef();
// Inflator registration and tracking
const inflatorId = useMemo(() => {
if (prevInflatorRef.current !== resolvedInflater) {
// Unregister?
if (inflatorIdRef.current) {
InflatorRepository.unregister(inflatorIdRef.current);
}
// Register
inflatorIdRef.current = generateId();
InflatorRepository.register(inflatorIdRef.current, resolvedInflater);
}
return inflatorIdRef.current;
}, [resolvedInflater]);
const wishlistContext = useMemo(() => ({
id: wishlistId.current,
inflatorId,
data
}), [inflatorId, data]);
return /*#__PURE__*/React.createElement(WishlistContext.Provider, {
value: wishlistContext
}, /*#__PURE__*/React.createElement(TemplatesRegistryContext.Provider, {
value: templatesRegistry
}, /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(View, {
style: styles.noDisplay
}, Object.keys(childrenTemplates).map(c => /*#__PURE__*/React.createElement(View, {
key: c + 'prerender'
}, /*#__PURE__*/React.createElement(TemplateContext.Provider, {
value: {
templateType: c,
renderChildren: true
}
}, childrenTemplates[c])))), /*#__PURE__*/React.createElement(InnerComponent, {
inflatorId: inflatorId,
style: style,
nativeWishlist: nativeWishlist,
rest: rest,
templates: childrenTemplates,
nestedTemplates: templatesRegistry.templates
}))));
}
const Component = /*#__PURE__*/React.forwardRef(ComponentBase);
function InnerComponent(_ref2) {
let {
inflatorId,
style,
nativeWishlist,
rest,
templates,
nestedTemplates
} = _ref2;
const combinedTemplates = {
...templates,
...nestedTemplates
};
const {
id
} = useWishlistContext();
const keys = Object.keys(combinedTemplates);
// console.log('@@@ Render WishList', inflatorId, keys.join(', '));
return /*#__PURE__*/React.createElement(NativeTemplateInterceptor, {
inflatorId: inflatorId,
style: style,
collapsable: false,
removeClippedSubviews: false
}, /*#__PURE__*/React.createElement(NativeWishList, {
style: styles.flex,
ref: nativeWishlist,
removeClippedSubviews: false,
inflatorId: inflatorId,
onEndReached: rest === null || rest === void 0 ? void 0 : rest.onEndReached,
onStartReached: rest === null || rest === void 0 ? void 0 : rest.onStartReached,
initialIndex: rest.initialIndex ?? 0
}), /*#__PURE__*/React.createElement(NativeTemplateContainer, {
names: keys,
inflatorId: inflatorId,
wishlistId: id,
key: Math.random().toString(),
collapsable: false
}, Object.keys(combinedTemplates).map(c => /*#__PURE__*/React.createElement(View, {
key: c
}, /*#__PURE__*/React.createElement(TemplateContext.Provider, {
value: {
templateType: c
}
}, combinedTemplates[c])))));
}
function Template(_ref3) {
let {
children,
type
} = _ref3;
const registry = useContext(TemplatesRegistryContext);
const templates = useContext(TemplateContext);
registry === null || registry === void 0 ? void 0 : registry.registerTemplate(type, children);
return templates !== null && templates !== void 0 && templates.renderChildren ? children : null;
}
Template.displayName = 'WishListTemplate';
export const Wishlist = {
Component,
Template,
Pressable,
View: WishlistView,
Image: WishlistImage,
Text: WishlistText,
IF,
Switch,
Case,
/**
* TODO(Szymon) It's just a prototype we have to think about matching new and old children
* TODO(Szymon) implement setChildren
*/
ForEach
};
const styles = StyleSheet.create({
flex: {
flex: 1
},
noDisplay: {
display: 'none'
},
offsetView: {
height: 0
}
});
//# sourceMappingURL=Wishlist.js.map