Repository: adobe/react-spectrum
File: packages/react-aria-components/src/Collection.tsx (TypeScript)

Code before:
      );
    }
  });
}

export function renderAfterDropIndicators(collection: ICollection<Node<unknown>>, node: Node<unknown>, renderDropIndicator: (target: ItemDropTarget) => ReactNode): ReactNode {
  let key = node.key;
  let keyAfter = collection.getKeyAfter(key);
  let nextItemInFlattenedCollection = keyAfter != null ? collection.getItem(keyAfter) : null;
  while (nextItemInFlattenedCollection != null && nextItemInFlattenedCollection.type !== 'item') {
    keyAfter = collection.getKeyAfter(nextItemInFlattenedCollection.key);
    nextItemInFlattenedCollection = keyAfter != null ? collection.getItem(keyAfter) : null;
  }

  let nextItemInSameLevel = node.nextKey != null ? collection.getItem(node.nextKey) : null;
  while (nextItemInSameLevel != null && nextItemInSameLevel.type !== 'item') {
    nextItemInSameLevel = nextItemInSameLevel.nextKey != null ? collection.getItem(nextItemInSameLevel.nextKey) : null;
  }

  // Render one or more "after" drop indicators when the next item in the flattened collection
  // has a smaller level, is not an item, or there are no more items in the collection.
  // Otherwise, the "after" position is equivalent to the next item's "before" position.
  let afterIndicators: ReactNode[] = [];
  if (nextItemInSameLevel == null) {
    let current: Node<unknown> | null = node;
    while (current && (!nextItemInFlattenedCollection || (current.parentKey !== nextItemInFlattenedCollection.parentKey && nextItemInFlattenedCollection.level < current.level))) {
      let indicator = renderDropIndicator({
        type: 'item',
        key: current.key,
        dropPosition: 'after'
      });
      if (isValidElement(indicator)) {
        afterIndicators.push(cloneElement(indicator, {key: `${current.key}-after`}));
      }
      current = current.parentKey != null ? collection.getItem(current.parentKey) : null;
    }
  }

  return afterIndicators;
}

export const CollectionRendererContext = createContext<CollectionRenderer>(DefaultCollectionRenderer);

type PersistedKeysReturnValue = Set<Key> | null;
export function usePersistedKeys(focusedKey: Key | null): PersistedKeysReturnValue {
  return useMemo(() => focusedKey != null ? new Set([focusedKey]) : null, [focusedKey]);
}


Review comment (suggestion):
We _might_ need to tweak to for the eventual tree with sections case? This is fine for now, I'm not really sure if `after` drop positions would ever become available for sections 

Code after:
      );
    }
  });
}

export function renderAfterDropIndicators(collection: ICollection<Node<unknown>>, node: Node<unknown>, renderDropIndicator: (target: ItemDropTarget) => ReactNode): ReactNode {
  let key = node.key;
  let keyAfter = collection.getKeyAfter(key);
  let nextItemInFlattenedCollection = keyAfter != null ? collection.getItem(keyAfter) : null;
  while (nextItemInFlattenedCollection != null && nextItemInFlattenedCollection.type !== 'item') {
    keyAfter = collection.getKeyAfter(nextItemInFlattenedCollection.key);
    nextItemInFlattenedCollection = keyAfter != null ? collection.getItem(keyAfter) : null;
  }

  let nextItemInSameLevel = node.nextKey != null ? collection.getItem(node.nextKey) : null;
  while (nextItemInSameLevel != null && nextItemInSameLevel.type !== 'item') {
    nextItemInSameLevel = nextItemInSameLevel.nextKey != null ? collection.getItem(nextItemInSameLevel.nextKey) : null;
  }

  // Render one or more "after" drop indicators when the next item in the flattened collection
  // has a smaller level, is not an item, or there are no more items in the collection.
  // Otherwise, the "after" position is equivalent to the next item's "before" position.
  let afterIndicators: ReactNode[] = [];
  if (nextItemInSameLevel == null) {
    let current: Node<unknown> | null = node;
    while (current?.type === 'item' && (!nextItemInFlattenedCollection || (current.parentKey !== nextItemInFlattenedCollection.parentKey && nextItemInFlattenedCollection.level < current.level))) {
      let indicator = renderDropIndicator({
        type: 'item',
        key: current.key,
        dropPosition: 'after'
      });
      if (isValidElement(indicator)) {
        afterIndicators.push(cloneElement(indicator, {key: `${current.key}-after`}));
      }
      current = current.parentKey != null ? collection.getItem(current.parentKey) : null;
    }
  }

  return afterIndicators;
}

export const CollectionRendererContext = createContext<CollectionRenderer>(DefaultCollectionRenderer);

type PersistedKeysReturnValue = Set<Key> | null;
export function usePersistedKeys(focusedKey: Key | null): PersistedKeysReturnValue {
  return useMemo(() => focusedKey != null ? new Set([focusedKey]) : null, [focusedKey]);
}