masonic
masonic copied to clipboard
fix(use-masonry): return children sorted by index
Changes from the PR add logic to sort children rendered in use-masonry
by their index.
This should help to maintain the tabbing order as the items will be rendered in a stable order - they often had a pretty random non-stable order before the change.
I considered doing it on our side in user code, but it's a bit hard to mutate use-masonry
output - the implementation would be fragile to future updates in masonic that could potentially change the rendering logic
example implementation fixing the issue in user code
function AwesomeMasonryComponent(props) {
// ...
return useMasonry({
...props
as: SortedMasonryItems,
});
}
const SortedMasonryItems = ({ children, ...props }: React.ComponentProps<'div'>) => {
const sortedChildren = React.Children.toArray(children).sort((a, b) => {
const indexA = getIndex(a);
const indexB = getIndex(b);
if (typeof indexA !== 'number' || typeof indexB !== 'number') {
return 0;
}
return indexA - indexB;
});
return <div {...props}>{sortedChildren}</div>;
};
function getIndex(
element: React.ReactChild | React.ReactFragment | React.ReactPortal,
): number | undefined {
// masonic renders children like that:
// <ItemComponent
// key={key}
// ref={setItemRef(index)}
// role={itemRole}
// style={
// typeof itemStyle === 'object'
// ? Object.assign({}, phaseOneStyle, itemStyle)
// : phaseOneStyle
// }
// >
// {createRenderElement(RenderComponent, index, data, columnWidth)}
// </ItemComponent>;
// so, we need to access the child of the child to get the index props and use it for sorting
if (!React.isValidElement(element)) {
return undefined;
}
const child = React.Children.only(
(element as React.ReactElement<{ children?: React.ReactNode }>).props.children,
);
if (React.isValidElement(child)) {
const index = child.props.index;
if (typeof index === 'number') {
return index;
}
}
return undefined;
}
closes #74
Thanks for submitting! I will give it a loot this weekend