diff --git a/src/app/features/lobby/Lobby.tsx b/src/app/features/lobby/Lobby.tsx index 069e925e..6851873f 100644 --- a/src/app/features/lobby/Lobby.tsx +++ b/src/app/features/lobby/Lobby.tsx @@ -162,6 +162,7 @@ export function Lobby() { const screenSize = useScreenSizeContext(); const [onTop, setOnTop] = useState(true); const [closedCategories, setClosedCategories] = useAtom(useClosedLobbyCategoriesAtom()); + const roomToParents = useAtomValue(roomToParentsAtom); const [sidebarItems] = useSidebarItems( useOrphanSpaces(mx, allRoomsAtom, useAtomValue(roomToParentsAtom)) ); @@ -193,6 +194,36 @@ export function Lobby() { [mx] ); + /** + * Recursively checks if a given parentId (or all its ancestors) is in a closed category. + * + * @param spaceId - The root space ID. + * @param parentId - The parent space ID to start the check from. + * @returns True if parentId or all ancestors is in a closed category. + */ + const getInClosedCategories = useCallback( + (spaceId: string, parentId: string): boolean => { + if (closedCategories.has(makeLobbyCategoryId(spaceId, parentId))) { + return true; + } + + const parentParentIds = roomToParents.get(parentId); + if (!parentParentIds || parentParentIds.size === 0) { + return false; + } + + let anyOpen = false; + parentParentIds.forEach((id) => { + if (!getInClosedCategories(spaceId, id)) { + anyOpen = true; + } + }); + + return !anyOpen; + }, + [closedCategories, roomToParents] + ); + const [draggingItem, setDraggingItem] = useState(); const hierarchy = useSpaceHierarchy( space.roomId, @@ -200,9 +231,9 @@ export function Lobby() { getRoom, useCallback( (childId) => - closedCategories.has(makeLobbyCategoryId(space.roomId, childId)) || + getInClosedCategories(space.roomId, childId) || (draggingItem ? 'space' in draggingItem : false), - [closedCategories, space.roomId, draggingItem] + [draggingItem, getInClosedCategories, space.roomId] ) ); @@ -476,14 +507,20 @@ export function Lobby() { const item = hierarchy[vItem.index]; if (!item) return null; const nextSpaceId = hierarchy[vItem.index + 1]?.space.roomId; - const categoryId = makeLobbyCategoryId(space.roomId, item.space.roomId); + const inClosedCategory = getInClosedCategories( + space.roomId, + item.space.roomId + ); + + const paddingLeft = `calc((${item.space.depth} - 1) * ${config.space.S200})`; return ( } > - } - onClick={handleAddSpace} - aria-pressed={!!cords} - > - Add Space - + {item.parentId === undefined ? ( + } + onClick={handleAddSpace} + aria-pressed={!!cords} + > + Add Space + + ) : ( + + Add Space + + } + > + {(triggerRef) => ( + + + + )} + + )} ); } @@ -473,7 +503,7 @@ export const SpaceItemCard = as<'div', SpaceItemCardProps>( {canEditChild && ( - {item.parentId === undefined && } + )} diff --git a/src/app/pages/client/space/Space.tsx b/src/app/pages/client/space/Space.tsx index a84c0e30..bb79dcac 100644 --- a/src/app/pages/client/space/Space.tsx +++ b/src/app/pages/client/space/Space.tsx @@ -314,11 +314,11 @@ export function Space() { ); /** - * Recursively checks if a given parentId (and its ancestors) is in a closed category. + * Recursively checks if a given parentId (or all its ancestors) is in a closed category. * * @param spaceId - The root space ID. * @param parentId - The parent space ID to start the check from. - * @returns True if parentId or any ancestor is in a closed category. + * @returns True if parentId or all ancestors is in a closed category. */ const getInClosedCategories = useCallback( (spaceId: string, parentId: string): boolean => { @@ -331,14 +331,14 @@ export function Space() { return false; } - let closed = false; + let anyOpen = false; parentParentIds.forEach((id) => { - if (getInClosedCategories(spaceId, id)) { - closed = true; + if (!getInClosedCategories(spaceId, id)) { + anyOpen = true; } }); - return closed; + return !anyOpen; }, [closedCategories, roomToParents] );