Skip to content

Commit

Permalink
[Composite] Derive sorted map state (#1489)
Browse files Browse the repository at this point in the history
  • Loading branch information
atomiks authored Feb 28, 2025
1 parent e05da23 commit 46ecc88
Showing 1 changed file with 13 additions and 35 deletions.
48 changes: 13 additions & 35 deletions packages/react/src/composite/list/CompositeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@
'use client';
import * as React from 'react';
import PropTypes from 'prop-types';
import { fastObjectShallowCompare } from '../../utils/fastObjectShallowCompare';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect';
import { CompositeListContext } from './CompositeListContext';
import { useEnhancedEffect } from '../../utils/useEnhancedEffect';

function sortByDocumentPosition(a: Node, b: Node) {
const position = a.compareDocumentPosition(b);
Expand All @@ -25,26 +24,6 @@ function sortByDocumentPosition(a: Node, b: Node) {

export type CompositeMetadata<CustomMetadata> = { index?: number | null } & CustomMetadata;

function areMapsEqual<Metadata>(
map1: Map<Node, CompositeMetadata<Metadata> | null>,
map2: Map<Node, CompositeMetadata<Metadata> | null>,
) {
if (map1.size !== map2.size) {
return false;
}
for (const [key, value] of map1.entries()) {
const value2 = map2.get(key);
// compare the index before comparing everything else
if (value?.index !== value2?.index) {
return false;
}
if (value2 !== undefined && !fastObjectShallowCompare(value, value2)) {
return false;
}
}
return true;
}

/**
* Provides context for a list of items in a composite component.
* @ignore - internal component.
Expand All @@ -66,26 +45,25 @@ function CompositeList<Metadata>(props: CompositeList.Props<Metadata>) {
});
}, []);

useEnhancedEffect(() => {
const newMap = new Map(map);

const nodes = Array.from(newMap.keys()).sort(sortByDocumentPosition);
const sortedMap = React.useMemo(() => {
const newMap = new Map<Node, CompositeMetadata<Metadata>>();
const sortedNodes = Array.from(map.keys()).sort(sortByDocumentPosition);

nodes.forEach((node, index) => {
sortedNodes.forEach((node, index) => {
const metadata = map.get(node) ?? ({} as CompositeMetadata<Metadata>);

newMap.set(node, { ...metadata, index });
});

if (!areMapsEqual(map, newMap)) {
setMap(newMap);
onMapChange?.(newMap);
}
}, [map, onMapChange]);
return newMap;
}, [map]);

useEnhancedEffect(() => {
onMapChange?.(sortedMap);
}, [sortedMap, onMapChange]);

const contextValue = React.useMemo(
() => ({ register, unregister, map, elementsRef, labelsRef }),
[register, unregister, map, elementsRef, labelsRef],
() => ({ register, unregister, map: sortedMap, elementsRef, labelsRef }),
[register, unregister, sortedMap, elementsRef, labelsRef],
);

return (
Expand Down

0 comments on commit 46ecc88

Please sign in to comment.