mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
141 lines
3.8 KiB
JavaScript
141 lines
3.8 KiB
JavaScript
// @flow
|
|
import React, {
|
|
useCallback,
|
|
useContext,
|
|
useLayoutEffect,
|
|
useRef,
|
|
useState,
|
|
} from 'react';
|
|
import { Menu, MenuList, MenuButton, MenuItem } from '@reach/menu-button';
|
|
import Button from '../Button';
|
|
import ButtonIcon from '../ButtonIcon';
|
|
import { TreeContext } from './TreeContext';
|
|
import { StoreContext } from '../context';
|
|
import { useIsOverflowing } from '../hooks';
|
|
|
|
import type { Element } from './types';
|
|
|
|
import styles from './OwnersStack.css';
|
|
|
|
export default function OwnerStack() {
|
|
const { ownerStack, ownerStackIndex, resetOwnerStack } = useContext(
|
|
TreeContext
|
|
);
|
|
|
|
const [elementsTotalWidth, setElementsTotalWidth] = useState(0);
|
|
const elementsBarRef = useRef<HTMLDivElement | null>(null);
|
|
const isOverflowing = useIsOverflowing(elementsBarRef, elementsTotalWidth);
|
|
|
|
useLayoutEffect(() => {
|
|
// If we're already overflowing, then we don't need to re-measure items.
|
|
// That's because once the owners stack is open, it can only get larger (by driling in).
|
|
// A totally new stack can only be reached by exiting this mode and re-entering it.
|
|
if (elementsBarRef.current === null || isOverflowing) {
|
|
return () => {};
|
|
}
|
|
|
|
let elementsTotalWidth = 0;
|
|
for (let i = 0; i < ownerStack.length; i++) {
|
|
const element = elementsBarRef.current.children[i];
|
|
const computedStyle = getComputedStyle(element);
|
|
|
|
elementsTotalWidth +=
|
|
element.offsetWidth +
|
|
parseInt(computedStyle.marginLeft, 10) +
|
|
parseInt(computedStyle.marginRight, 10);
|
|
}
|
|
|
|
setElementsTotalWidth(elementsTotalWidth);
|
|
}, [elementsBarRef, isOverflowing, ownerStack.length]);
|
|
|
|
return (
|
|
<div className={styles.OwnerStack}>
|
|
<Button
|
|
className={styles.IconButton}
|
|
onClick={resetOwnerStack}
|
|
title="Back to tree view"
|
|
>
|
|
<ButtonIcon type="close" />
|
|
</Button>
|
|
<div className={styles.VRule} />
|
|
<div className={styles.Bar} ref={elementsBarRef}>
|
|
{isOverflowing && (
|
|
<ElementsDropdown
|
|
ownerStack={ownerStack}
|
|
ownerStackIndex={ownerStackIndex}
|
|
/>
|
|
)}
|
|
{isOverflowing ? (
|
|
<ElementView
|
|
id={ownerStack[((ownerStackIndex: any): number)]}
|
|
index={ownerStackIndex}
|
|
/>
|
|
) : (
|
|
ownerStack.map((id, index) => (
|
|
<ElementView key={id} id={id} index={index} />
|
|
))
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
type ElementsDropdownProps = {
|
|
ownerStack: Array<number>,
|
|
ownerStackIndex: number | null,
|
|
};
|
|
function ElementsDropdown({
|
|
ownerStack,
|
|
ownerStackIndex,
|
|
}: ElementsDropdownProps) {
|
|
const store = useContext(StoreContext);
|
|
const { selectOwner } = useContext(TreeContext);
|
|
|
|
return (
|
|
<Menu>
|
|
<MenuButton className={styles.MenuButton} title="Open elements dropdown">
|
|
<ButtonIcon type="more" />
|
|
</MenuButton>
|
|
<MenuList className={styles.Modal}>
|
|
{ownerStack.map((id, index) => (
|
|
<MenuItem
|
|
key={id}
|
|
className={styles.Component}
|
|
onSelect={() => selectOwner(id)}
|
|
>
|
|
{((store.getElementByID(id): any): Element).displayName}
|
|
</MenuItem>
|
|
))}
|
|
</MenuList>
|
|
</Menu>
|
|
);
|
|
}
|
|
|
|
type ElementViewProps = {
|
|
id: number,
|
|
index: number | null,
|
|
};
|
|
function ElementView({ id, index }: ElementViewProps) {
|
|
const store = useContext(StoreContext);
|
|
const { ownerStackIndex, selectOwner } = useContext(TreeContext);
|
|
|
|
const { displayName } = ((store.getElementByID(id): any): Element);
|
|
|
|
const isSelected = ownerStackIndex === index;
|
|
|
|
const handleClick = useCallback(() => {
|
|
if (!isSelected) {
|
|
selectOwner(id);
|
|
}
|
|
}, [id, isSelected, selectOwner]);
|
|
|
|
return (
|
|
<button
|
|
className={isSelected ? styles.SelectedComponent : styles.Component}
|
|
onClick={handleClick}
|
|
>
|
|
{displayName}
|
|
</button>
|
|
);
|
|
}
|