feat: add more correct onRender callback for sheets (#1681)

This commit is contained in:
Tobias Åström
2025-02-25 16:52:48 +01:00
committed by GitHub
parent 8562f766dd
commit a5c8e41458
6 changed files with 79 additions and 7 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useState, useContext, useMemo, forwardRef, useImperativeHandle } from 'react';
import React, { useEffect, useState, useContext, useMemo, forwardRef, useImperativeHandle, useRef } from 'react';
import useLayout from '../hooks/useLayout';
import getObject from '../object/get-object';
import Cell from './Cell';
@@ -17,7 +17,7 @@ const SheetElement = {
className: 'njs-sheet',
};
function getCellRenderer(cell, halo, initialSnOptions, initialSnPlugins, initialError, onMount, navigation) {
function getCellRenderer(cell, halo, initialSnOptions, initialSnPlugins, initialError, onMount, navigation, onError) {
const { x, y, width, height } = cell.bounds;
return (
<div
@@ -34,6 +34,7 @@ function getCellRenderer(cell, halo, initialSnOptions, initialSnPlugins, initial
initialError={initialError}
onMount={onMount}
navigation={navigation}
onError={onError}
/>
</div>
);
@@ -53,7 +54,17 @@ function getBounds(pos, columns, rows) {
const Sheet = forwardRef(
(
{ model: inputModel, halo, initialSnOptions, initialSnPlugins, initialError, onMount, unmount, navigation },
{
model: inputModel,
halo,
initialSnOptions,
initialSnPlugins,
initialError,
onMount,
unmount,
navigation,
onError,
},
ref
) => {
const { root } = halo;
@@ -64,6 +75,8 @@ const Sheet = forwardRef(
const [bgColor, setBgColor] = useState(undefined);
const [bgImage, setBgImage] = useState(undefined);
const [deepHash, setDeepHash] = useState('');
const renderState = useRef({ cellCount: 0, cellsRendered: 0, initialRender: false });
navigation?.setCurrentSheetId?.(model.id);
/// For each object
useEffect(() => {
@@ -85,6 +98,17 @@ const Sheet = forwardRef(
});
const lCells = layout.cells;
renderState.cellCount = lCells.length;
renderState.cellsRendered = 0;
const renderCallback = () => {
renderState.cellsRendered++;
if (renderState.cellsRendered === renderState.cellCount && !renderState.initialRender) {
renderState.initialRender = true;
initialSnOptions.onInitialRender();
}
};
const { columns, rows } = layout;
// TODO - should try reuse existing objects on subsequent renders
// Non-id updates should only change the "css"
@@ -110,6 +134,12 @@ const Sheet = forwardRef(
currentId: uid(),
mounted,
mountedPromise,
options: {
...initialSnOptions,
...{
onInitialRender: renderCallback,
},
},
};
})
);
@@ -133,7 +163,7 @@ const Sheet = forwardRef(
() =>
cells
? cells.map((c) =>
getCellRenderer(c, halo, initialSnOptions, initialSnPlugins, initialError, c.mounted, navigation)
getCellRenderer(c, halo, c.options, initialSnPlugins, initialError, c.mounted, navigation, onError)
)
: [],
[cells]

View File

@@ -11,6 +11,7 @@ export default function glue({
onMount,
initialError,
navigation,
onError,
}) {
const { root } = halo;
const sheetRef = React.createRef();
@@ -29,6 +30,7 @@ export default function glue({
onMount={onMount}
unmount={unmount}
navigation={navigation}
onError={onError}
/>,
element,
model.id

View File

@@ -19,7 +19,7 @@ export default async function getObject(
const navigation =
inputNavigation || (model.genericType === 'sheet' ? createNavigationApi(halo, store, model) : undefined);
if (model.genericType === 'sheet') {
return initSheet(model, { options, plugins, element }, halo, navigation);
return initSheet(model, { options, plugins, element, onRender, onError }, halo, navigation);
}
return init(model, { options, plugins, element, onRender, onError }, halo, navigation);

View File

@@ -2,12 +2,15 @@
import sheetAPI from '../sheet';
export default async function initSheet(model, optional, halo, navigation, initialError, onDestroy = async () => {}) {
const { onRender, onError } = optional;
const api = sheetAPI({
model,
halo,
navigation,
initialError,
onDestroy,
onRender,
onError,
});
if (optional.options) {
api.__DO_NOT_USE__.options(optional.options);

View File

@@ -5,25 +5,49 @@ import getPatches from './utils/patcher';
const noopi = () => {};
export default function sheet({ model, halo, navigation, initialError, onDestroy = async () => {} } = {}) {
export default function sheet({
model,
halo,
navigation,
initialError,
onDestroy = async () => {},
onRender = () => {},
onError = () => {},
} = {}) {
let unmountSheet = noopi;
let sheetRef = null;
let mountedReference = null;
let onMount = null;
let onRenderResolve = null;
const mounted = new Promise((resolve) => {
onMount = resolve;
});
const rendered = new Promise((resolve) => {
onRenderResolve = resolve;
});
const createOnInitialRender = (override) => () => {
override?.(); // from options.onInitialRender
onRenderResolve(); // internal promise in viz to wait for render
onRender(); // from RenderConfig
};
let initialSnOptions = {};
let initialSnPlugins = [];
const setSnOptions = async (opts) => {
const override = opts.onInitialRender;
if (mountedReference) {
(async () => {
await mounted;
sheetRef.current.setSnOptions({
...initialSnOptions,
...opts,
...{
onInitialRender: createOnInitialRender(override),
},
});
})();
} else {
@@ -31,6 +55,9 @@ export default function sheet({ model, halo, navigation, initialError, onDestroy
initialSnOptions = {
...initialSnOptions,
...opts,
...{
onInitialRender: createOnInitialRender(override),
},
};
}
};
@@ -79,6 +106,15 @@ export default function sheet({ model, halo, navigation, initialError, onDestroy
* @type {Navigation}
*/
navigation,
/**
* Gets the specific api that a Viz exposes.
* @private currently empty and private
* @returns {Promise<object>} object that contains the internal Viz api.
*/
async getImperativeHandle() {
await rendered;
return sheetRef.current.getImperativeHandle();
},
/**
* Destroys the sheet and removes it from the the DOM.
* @example
@@ -112,6 +148,7 @@ export default function sheet({ model, halo, navigation, initialError, onDestroy
initialError,
onMount,
navigation,
onError,
});
return mounted;
},

View File

@@ -189,4 +189,4 @@
}
}
}
}
}