react-native-wishlist
Version:
The fastest List component for React Native.
177 lines (170 loc) • 6.15 kB
JavaScript
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
import React, { forwardRef, useMemo } from 'react';
import { StyleSheet, Text } from 'react-native';
import { TemplateCallback } from './EventHandler';
import { ForEachBase } from './Components/ForEachBase';
import InflatorRepository from './InflatorRepository';
import { CaseBase } from './Components/Switch';
import { useTemplateContext } from './TemplateContext';
import { createTemplateValue, isTemplateValue } from './TemplateValue';
import { generateId } from './Utils';
import { useWishlistContext } from './WishlistContext';
// This is based on types from @types/react-native createAnimatedComponent.
function setInObject(obj, path, value) {
'worklet';
let current = obj;
for (let i = 0; i < path.length - 1; i++) {
current[path[i]] = current[path[i]] ?? {};
current = current[path[i]];
}
current[path[path.length - 1]] = value;
}
function traverseObject(obj, callback) {
const stack = [{
path: [],
value: obj
}];
while (stack.length > 0) {
const {
path,
value
} = stack.pop();
if (value && typeof value === 'object' && !isTemplateValue(value) && !(value instanceof TemplateCallback) && (path.length === 0 || path[path.length - 1] !== 'children')) {
Object.keys(value).forEach(key => {
stack.push({
path: [...path, key],
value: value[key]
});
});
} else {
callback(path, value);
}
}
}
function convertToTemplateValue(value, path) {
let curTemplateType = value;
return {
// TODO(janic): Need to call remove for template values created here.
templateValue: createTemplateValue(() => {
'worklet';
return curTemplateType;
}),
targetPath: path
};
}
export function createTemplateComponent(Component, addProps) {
const WishListComponent = /*#__PURE__*/forwardRef((_ref, ref) => {
let {
style,
...props
} = _ref;
const {
inflatorId
} = useWishlistContext();
const {
templateType
} = useTemplateContext();
const nativeId = useMemo(generateId, []);
const otherPropsMemoized = useMemo(() => {
const resolvedStyle = StyleSheet.flatten(style);
const templateValues = [];
const templateCallbacks = [];
const otherProps = {};
traverseObject({
...props,
style: resolvedStyle
}, (path, value) => {
const applyHacks = () => {
// Text component needs to receive a string child to work properly.
// @ts-expect-error TODO: fix this.
if (path[0] === 'children' && Component === Text) {
setInObject(otherProps, path, ' ');
}
};
if (isTemplateValue(value)) {
templateValues.push({
templateValue: value,
targetPath: path
});
applyHacks();
} else if (value instanceof TemplateCallback) {
templateCallbacks.push({
worklet: value.worklet,
// Callbacks should never be in objects.
eventName: value.eventName ?? path[0].replace(/^on/, 'top')
});
// Events have a boolean prop associated to know whether the
// function is set or not, so we still want to pass the prop.
setInObject(otherProps, path, () => {});
} else {
// @ts-expect-error TODO: fix this.
if (Component === ForEachBase && path[0] === 'template') {
templateValues.push(convertToTemplateValue(value, path));
}
if (
// @ts-expect-error TODO: fix this.
Component === CaseBase && path[0] === 'value' && !isTemplateValue(value)) {
templateValues.push(convertToTemplateValue(value, path));
}
if (
// @ts-expect-error TODO: fix this.
Component === Text && path[0] === 'children' && !isTemplateValue(value)) {
templateValues.push(convertToTemplateValue(value, path));
}
setInObject(otherProps, path, value);
}
});
InflatorRepository.registerMapping(inflatorId, nativeId, templateType, (value, templateItem, pool, rootValue) => {
'worklet';
templateValues.forEach(_ref2 => {
let {
templateValue
} = _ref2;
templateValue.__setDirty();
});
const propsToSet = {};
templateValues.forEach(_ref3 => {
let {
templateValue,
targetPath
} = _ref3;
setInObject(propsToSet, targetPath, templateValue.value());
});
templateCallbacks.forEach(_ref4 => {
let {
eventName,
worklet
} = _ref4;
templateItem.setCallback(eventName, ev => {
worklet(ev, value, rootValue);
});
});
// Styles need to be passed as props.
const {
style: styleForProps,
...otherPropsToSet
} = propsToSet;
const finalPropsToSet = {
...otherPropsToSet,
...styleForProps
};
if (addProps) {
addProps(templateItem, finalPropsToSet, inflatorId, pool, rootValue);
} else {
templateItem.addProps(finalPropsToSet);
}
});
return otherProps;
// TODO: This will change on every render, if we want this memo to work properly we need
// to shallow compare the props object.
}, [inflatorId, nativeId, props, style, templateType]);
// @ts-expect-error: this is ok.
return /*#__PURE__*/React.createElement(Component, _extends({}, otherPropsMemoized, {
ref: ref,
nativeID: nativeId
}));
});
WishListComponent.displayName = `WishList(${Component.displayName})`;
return WishListComponent;
}
//# sourceMappingURL=createTemplateComponent.js.map