implements intersection observer to mount fields over time
This commit is contained in:
@@ -1,9 +1,14 @@
|
||||
import React, { createContext, useEffect, useContext, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent';
|
||||
import useIntersect from '../../../hooks/useIntersect';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const intersectionObserverOptions = {
|
||||
rootMargin: '1000px',
|
||||
};
|
||||
|
||||
const RenderedFieldContext = createContext({});
|
||||
|
||||
export const useRenderedFields = () => useContext(RenderedFieldContext);
|
||||
@@ -20,6 +25,10 @@ const RenderFields = (props) => {
|
||||
operation: operationFromProps,
|
||||
} = props;
|
||||
|
||||
const [hasIntersected, setHasIntersected] = useState(false);
|
||||
const [intersectionRef, entry] = useIntersect(intersectionObserverOptions);
|
||||
const isIntersecting = Boolean(entry?.isIntersecting);
|
||||
|
||||
const { customComponentsPath: customComponentsPathFromContext, operation: operationFromContext } = useRenderedFields();
|
||||
|
||||
const operation = operationFromProps || operationFromContext;
|
||||
@@ -37,75 +46,85 @@ const RenderFields = (props) => {
|
||||
});
|
||||
}, [operation, customComponentsPath]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isIntersecting && !hasIntersected) {
|
||||
setHasIntersected(true);
|
||||
}
|
||||
}, [isIntersecting, hasIntersected]);
|
||||
|
||||
if (fieldSchema) {
|
||||
return (
|
||||
<RenderedFieldContext.Provider value={contextValue}>
|
||||
{fieldSchema.map((field, i) => {
|
||||
if (!field?.hidden && field?.admin?.disabled !== true) {
|
||||
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
|
||||
const FieldComponent = field?.admin?.hidden ? fieldTypes.hidden : fieldTypes[field.type];
|
||||
<div ref={intersectionRef}>
|
||||
{hasIntersected && (
|
||||
<RenderedFieldContext.Provider value={contextValue}>
|
||||
{fieldSchema.map((field, i) => {
|
||||
if (!field?.hidden && field?.admin?.disabled !== true) {
|
||||
if ((filter && typeof filter === 'function' && filter(field)) || !filter) {
|
||||
const FieldComponent = field?.admin?.hidden ? fieldTypes.hidden : fieldTypes[field.type];
|
||||
|
||||
let initialFieldData;
|
||||
let fieldPermissions = permissions[field.name];
|
||||
let initialFieldData;
|
||||
let fieldPermissions = permissions[field.name];
|
||||
|
||||
if (!field.name) {
|
||||
initialFieldData = initialData;
|
||||
fieldPermissions = permissions;
|
||||
} else if (initialData?.[field.name] !== undefined) {
|
||||
initialFieldData = initialData[field.name];
|
||||
}
|
||||
if (!field.name) {
|
||||
initialFieldData = initialData;
|
||||
fieldPermissions = permissions;
|
||||
} else if (initialData?.[field.name] !== undefined) {
|
||||
initialFieldData = initialData[field.name];
|
||||
}
|
||||
|
||||
let { admin: { readOnly } = {} } = field;
|
||||
let { admin: { readOnly } = {} } = field;
|
||||
|
||||
if (readOnlyOverride) readOnly = true;
|
||||
if (readOnlyOverride) readOnly = true;
|
||||
|
||||
if (permissions?.[field?.name]?.read?.permission !== false) {
|
||||
if (permissions?.[field?.name]?.[operation]?.permission === false) {
|
||||
readOnly = true;
|
||||
if (permissions?.[field?.name]?.read?.permission !== false) {
|
||||
if (permissions?.[field?.name]?.[operation]?.permission === false) {
|
||||
readOnly = true;
|
||||
}
|
||||
|
||||
if (FieldComponent) {
|
||||
return (
|
||||
<RenderCustomComponent
|
||||
key={field.name || `field-${i}`}
|
||||
path={`${customComponentsPath}${field.name ? `${field.name}.field` : ''}`}
|
||||
DefaultComponent={FieldComponent}
|
||||
componentProps={{
|
||||
...field,
|
||||
path: field.path || field.name,
|
||||
fieldTypes,
|
||||
initialData: initialFieldData,
|
||||
admin: {
|
||||
...(field.admin || {}),
|
||||
readOnly,
|
||||
},
|
||||
permissions: fieldPermissions,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="missing-field"
|
||||
key={i}
|
||||
>
|
||||
No matched field found for
|
||||
{' '}
|
||||
"
|
||||
{field.label}
|
||||
"
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (FieldComponent) {
|
||||
return (
|
||||
<RenderCustomComponent
|
||||
key={field.name || `field-${i}`}
|
||||
path={`${customComponentsPath}${field.name ? `${field.name}.field` : ''}`}
|
||||
DefaultComponent={FieldComponent}
|
||||
componentProps={{
|
||||
...field,
|
||||
path: field.path || field.name,
|
||||
fieldTypes,
|
||||
initialData: initialFieldData,
|
||||
admin: {
|
||||
...(field.admin || {}),
|
||||
readOnly,
|
||||
},
|
||||
permissions: fieldPermissions,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="missing-field"
|
||||
key={i}
|
||||
>
|
||||
No matched field found for
|
||||
{' '}
|
||||
"
|
||||
{field.label}
|
||||
"
|
||||
</div>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
</RenderedFieldContext.Provider>
|
||||
return null;
|
||||
})}
|
||||
</RenderedFieldContext.Provider>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
29
src/client/hooks/useIntersect.js
Normal file
29
src/client/hooks/useIntersect.js
Normal file
@@ -0,0 +1,29 @@
|
||||
/* eslint-disable no-shadow */
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
export default ({ root = null, rootMargin, threshold = 0 } = {}) => {
|
||||
const [entry, updateEntry] = useState({});
|
||||
const [node, setNode] = useState(null);
|
||||
|
||||
const observer = useRef(
|
||||
new window.IntersectionObserver(([entry]) => updateEntry(entry), {
|
||||
root,
|
||||
rootMargin,
|
||||
threshold,
|
||||
}),
|
||||
);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const { current: currentObserver } = observer;
|
||||
currentObserver.disconnect();
|
||||
|
||||
if (node) currentObserver.observe(node);
|
||||
|
||||
return () => currentObserver.disconnect();
|
||||
},
|
||||
[node],
|
||||
);
|
||||
|
||||
return [setNode, entry];
|
||||
};
|
||||
Reference in New Issue
Block a user