@mui/material#Popper TypeScript Examples
The following examples show how to use
@mui/material#Popper.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ThreePoints.tsx From your_spotify with GNU General Public License v3.0 | 6 votes |
export default function ThreePoints({ items }: ThreePointsProps) {
const [open, setOpen] = useState(false);
const buttonRef = useRef<HTMLButtonElement | null>(null);
const internClick = useCallback(
(index: number) => {
items[index].onClick();
setOpen(false);
},
[items],
);
return (
<ClickAwayListener onClickAway={() => setOpen(false)}>
<div>
<IconButton size="small" ref={buttonRef} onClick={() => setOpen((v) => !v)}>
<MoreHoriz fontSize="small" />
</IconButton>
<Popper
open={open}
anchorEl={buttonRef.current}
popperOptions={{ placement: 'auto-start' }}
transition>
{({ TransitionProps }) => (
// eslint-disable-next-line react/jsx-props-no-spreading
<Fade {...TransitionProps}>
<div className={s.popper}>
{items.map((item, index) => (
<Tooltip title={(item.disabled && item.disabledTooltip) || ''}>
<Button
// eslint-disable-next-line react/no-array-index-key
key={index}
variant="text"
disabled={item.disabled}
className={clsx({
[s.item]: true,
[s[item.style ?? 'normal']]: true,
})}
onClick={() => internClick(index)}>
{item.label}
</Button>
</Tooltip>
))}
{items.length === 0 && (
<Button
// eslint-disable-next-line react/no-array-index-key
variant="text"
disabled>
No action available
</Button>
)}
</div>
</Fade>
)}
</Popper>
</div>
</ClickAwayListener>
);
}
Example #2
Source File: HeaderSearch.tsx From Cromwell with MIT License | 5 votes |
render() {
const { isLoading, searchItems, searchOpen } = this.state;
return (
<>
<TextField label="Search..."
variant="outlined" size="small"
ref={this.searchAnchorRef}
// onBlur={handleSearchClose}
onChange={(event) => this.handleSearchInput(event.currentTarget.value)} />
<Popper open={searchOpen} anchorEl={this.searchAnchorRef.current}
style={{ zIndex: 9999 }}
transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<div>
<ClickAwayListener onClickAway={this.handleSearchClose}>
<div className={styles.searchContent} onClick={this.handleSearchClose}>
{isLoading && (
<LoadBox size={100} />
)}
{!isLoading && searchItems.length === 0 && (
<p className={styles.notFoundText}>No items found</p>
)}
{!isLoading && searchItems.map(post => {
return (
<Grid container className={styles.listItem} key={post.id}>
<Link href={`/post/${post.slug}`} >
<Grid item xs={12} className={styles.itemMain}>
<div
style={{ backgroundImage: `url(${post?.mainImage})` }}
className={styles.itemImage}
></div>
<div className={styles.itemMainInfo}>
<p className={styles.itemTitle}>{post.title ?? ''}</p>
</div>
</Grid>
</Link>
</Grid>
)
})}
</div>
</ClickAwayListener>
</div>
</Fade>
)}
</Popper>
</>
);
}
Example #3
Source File: GroupMemberSelect.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
StyledAutocompletePopper = styled(Popper)(({ theme }) => ({
minWidth: 200,
}))
Example #4
Source File: AccountSelect.tsx From abrechnung with GNU Affero General Public License v3.0 | 5 votes |
StyledAutocompletePopper = styled(Popper)(({ theme }) => ({
minWidth: 200,
}))
Example #5
Source File: Autocomplete.tsx From Cromwell with MIT License | 4 votes |
render() {
const { searchOpen, pickedItems, pickedText } = this.state;
const { multiple } = this.props;
return (
<>
<MuiAutocomplete
className={this.props.className}
multiple={multiple}
options={pickedItems ?? []}
getOptionLabel={(option) => option as any}
value={multiple ? (pickedItems ?? []) : (pickedText ?? '')}
onChange={(event, newValue) => {
if (!newValue) {
this.handleClear();
}
if (multiple && newValue) {
const pickedItems = [...new Set([...(newValue as any)])];
this.props.onSelect?.(pickedItems.map(item => this.pickedData[item]));
Object.values(this.multiSelectionListeners).forEach(func => func(pickedItems));
this.setState({
pickedItems,
});
}
}}
PopperComponent={() => <></>}
renderInput={(params) => (
<TextField
{...params}
value={this.state.searchText ?? ''}
onChange={(event) => this.handleSearchInput(event.currentTarget.value)}
onFocus={() => this.handleSearchInput(this.state.searchText)}
onBlur={() => !multiple && this.handleSearchClose()}
label={this.props.label ?? "Search..."}
fullWidth={this.props.fullWidth}
ref={this.searchAnchorRef}
size="small"
variant={this.props.variant ?? 'standard'}
/>
)}
/>
<Popper open={searchOpen} anchorEl={this.searchAnchorRef.current}
style={{ zIndex: 9999 }}
transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<div className={styles.searchContent}>
<ClickAwayListener onClickAway={this.handleSearchClose}>
{/* {isLoading && (
<LoadBox size={100} />
)}
{!isLoading && searchItems.length === 0 && (
<p className={styles.notFoundText}>No items found</p>
)}
{!isLoading && ( */}
<CList<TItemData, ListItemProps<TItemData>>
useAutoLoading
className={styles.list}
id={this.listId}
loader={this.loadMore}
elements={{ preloader: <div className={styles.listPreloader}>{this.listSkeleton}</div> }}
ListItem={ListItem}
listItemProps={{
handleItemClick: this.handleItemClick,
getOptionLabel: this.props.getOptionLabel,
getOptionValue: this.props.getOptionValue,
pickedItems: this.state?.pickedItems,
multiple,
ItemComponent: this.props.itemComponent,
addMultiSelectListener: this.addMultiSelectListener,
removeMultiSelectListener: this.removeMultiSelectListener,
}}
/>
{/* )} */}
</ClickAwayListener>
</div>
</Fade>
)}
</Popper>
</>
);
}
Example #6
Source File: AttributesTab.tsx From Cromwell with MIT License | 4 votes |
export default function AttributesTab(props: {
product: TProduct;
attributes: TAttribute[];
setProdData: (data: TProduct) => void;
forceUpdate: () => void;
}) {
const { product, attributes, setProdData, forceUpdate } = props;
const [popperAnchorEl, setPopperAnchorEl] = React.useState<HTMLButtonElement | null>(null);
const [popperOpen, setPopperOpen] = React.useState(false);
const leftAttributesToAdd: TAttribute[] = [];
attributes.forEach(attr => {
if (product) {
const hasAttr = product.attributes ? product.attributes.some(a => a.key === attr.key) : false;
if (!hasAttr) {
leftAttributesToAdd.push(attr);
}
}
});
const addAttribute = (key: string) => {
const prod: TProduct = JSON.parse(JSON.stringify(product));
if (!prod.attributes) prod.attributes = [];
prod.attributes.push({
key,
values: []
})
setPopperOpen(false);
setProdData(prod);
forceUpdate();
}
const deleteAttribute = (index: number) => {
const prod: TProduct = JSON.parse(JSON.stringify(product));
if (prod.attributes) {
prod.attributes = prod.attributes.filter((a, i) => i !== index);
setProdData(prod);
forceUpdate();
}
}
return (
<div>
{product.attributes && attributes && (
product.attributes.map((prodAttr, prodAttrIdx) => {
let origAttr: TAttribute | undefined = undefined;
for (const attr of attributes) {
if (attr.key === prodAttr.key) origAttr = attr;
}
if (origAttr) {
const leftValues = origAttr.values.filter(v => !prodAttr.values.some(pv => pv.value === v.value))
const rightValues = prodAttr.values.map(v => v.value);
return (
<div className={styles.attributeBlock} key={prodAttr.key}>
<div className={styles.attributeHeader}>
<p className={styles.tag}>{prodAttr.key}</p>
<Tooltip title="Delete attribute">
<IconButton
aria-label="delete attribute"
onClick={() => deleteAttribute(prodAttrIdx)}
><DeleteForeverIcon />
</IconButton>
</Tooltip>
</div>
<TransferList
left={leftValues.map(v => v.value)}
setLeft={() => { }}
right={rightValues}
itemComp={(props) => (
<div className={styles.attributeInstanceValue}>
<p>{props.value}</p>
</div>
)}
setRight={(val) => {
const prod: TProduct = JSON.parse(JSON.stringify(product));
if (!prod.attributes) prod.attributes = [];
const newVals: TAttributeInstanceValue[] = [];
val.forEach(newVal => {
let hasVal = false;
prod.attributes[prodAttrIdx].values.forEach(prodVal => {
if (prodVal.value === newVal) {
newVals.push(prodVal);
hasVal = true;
}
})
if (!hasVal) {
newVals.push({
value: newVal
});
}
});
prod.attributes[prodAttrIdx].values = newVals.sort((a, b) => a.value > b.value ? 1 : -1);
setProdData(prod);
forceUpdate();
}}
/>
</div>
)
}
})
)}
<Box sx={{ my: 4 }} style={{ width: '100%', display: 'flex' }}>
<Button
sx={{ mx: 'auto', display: 'block' }}
variant="contained"
color="primary"
onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
setPopperAnchorEl(event.currentTarget);
setPopperOpen((prev) => !prev);
}}
disabled={!leftAttributesToAdd.length}
>Add attribute</Button>
</Box>
<Popper open={popperOpen} anchorEl={popperAnchorEl} placement={'bottom-start'} transition>
{({ TransitionProps }) => (
<Fade {...TransitionProps} timeout={350}>
<Paper className={styles.newAttributesList}>
{leftAttributesToAdd.map(attr => {
return (
<MenuItem
key={attr.key}
onClick={() => addAttribute(attr.key)}
className={styles.newAttributeOption}
>{attr.key}</MenuItem>
)
})}
</Paper>
</Fade>
)}
</Popper>
</div>
)
}
Example #7
Source File: Sider.tsx From your_spotify with GNU General Public License v3.0 | 4 votes |
export default function Sider({ className }: SiderProps) {
const dispatch = useAppDispatch();
const user = useSelector(selectUser);
const location = useLocation();
const inputRef = useRef<HTMLInputElement | null>(null);
const [search, setSearch] = useState('');
const results = useConditionalAPI(search.length >= 3, api.searchArtists, search);
const reset = useCallback(() => {
setSearch('');
}, []);
const copyCurrentPage = useCallback(() => {
if (!user?.publicToken) {
dispatch(
alertMessage({
level: 'error',
message: 'No public token generated, go to the settings page to generate one',
}),
);
return;
}
dispatch(
alertMessage({
level: 'info',
message: 'Copied current page to clipboard with public token',
}),
);
}, [dispatch, user?.publicToken]);
const toCopy = useShareLink();
return (
<div className={clsx(s.root, className)}>
<Link to="/" className={s.title}>
<Text onDark element="h1">
Your Spotify
</Text>
</Link>
<input
className={s.input}
placeholder="Search..."
value={search}
onChange={(ev) => setSearch(ev.target.value)}
ref={inputRef}
/>
<Popper
open={search.length > 0}
anchorEl={inputRef.current}
placement="bottom"
className={s.popper}>
<Paper className={s.results} style={{ width: inputRef.current?.clientWidth }}>
{search.length < 3 && <Text element="strong">At least 3 characters</Text>}
{results?.length === 0 && <Text element="strong">No results found</Text>}
{results?.map((res) => (
<Link to={`/artist/${res.id}`} className={s.result} key={res.id} onClick={reset}>
<img className={s.resultimage} src={getImage(res)} alt="Artist" />
<Text element="strong">{res.name}</Text>
</Link>
))}
</Paper>
</Popper>
<nav>
{links.map((category) => (
<div className={s.category} key={category.label}>
<Text element="div" onDark className={s.categoryname}>
{category.label}
</Text>
{toCopy &&
category.items.map((link) => {
if (link.link === '/share') {
return (
<CopyToClipboard key={link.label} onCopy={copyCurrentPage} text={toCopy}>
<div className={s.link} key={link.label}>
<Text onDark>
{location.pathname === link.link ? link.iconOn : link.icon}
</Text>
<Text onDark>{link.label}</Text>
</div>
</CopyToClipboard>
);
}
return (
<Link to={link.link} className={s.link} key={link.label}>
<Text onDark>{location.pathname === link.link ? link.iconOn : link.icon}</Text>
<Text onDark>{link.label}</Text>
</Link>
);
})}
</div>
))}
</nav>
</div>
);
}
Example #8
Source File: engineSelectPopper.tsx From Search-Next with GNU General Public License v3.0 | 4 votes |
EngineSelectPopper: FC<EngineSelectPopperProps> = (props) => {
const {
width = 300,
anchorEl,
open = false,
onBtnClick,
onEngineSelect,
engine,
} = props;
const [classifyEngineList, setClassifyEngineList] = useState<
SearchEngineClassifyWithChildren[]
>([]);
const [selected, setSelected] = useState<string>('');
const [engineList, setEngineList] = React.useState([] as SearchEngine[]);
const getClassifyEngine = () => {
getClassifyEngineListApi().then((res) => {
const current = {
...engine.engine,
classifyId: engine.engine?.classify?._id,
} as SearchEngine;
let filterEngines = res
.map((i) => i.children)
.flat()
.filter((u) => u._id !== engine.engine?._id)
.slice(0, engine.indexCount - 1);
if (engine.sortType === 'count') {
filterEngines = filterEngines.sort((r, t) => t.count - r.count);
}
setEngineList([current, ...filterEngines]);
setClassifyEngineList(res);
res.length > 0 && setSelected(res[0]._id);
});
};
useEffect(() => {
if (engine?.engine?.classify?._id) {
setSelected(engine?.engine?.classify?._id);
}
getClassifyEngine();
}, [engine]);
return (
<div className="mb-1">
<Popper open={open} anchorEl={anchorEl} placement="top">
{({ TransitionProps }) => (
<Card
{...TransitionProps}
style={{ width: `${anchorEl?.clientWidth}px` }}
className="mb-1"
>
<div className="p-2 flex gap-2 items-start">
<div className="max-h-20 overflow-y-auto pr-1">
{classifyEngineList.map((item) => (
<div
key={item._id}
onClick={() => {
setSelected(item._id);
}}
className={classnames(
'px-1.5 py-0.5 cursor-pointer rounded text-sm',
selected === item._id
? 'bg-primary text-white'
: 'bg-white',
)}
>
{item.name}
</div>
))}
</div>
<div className="flex gap-1 items-start justify-start flex-grow">
{classifyEngineList
.filter((i) => i._id === selected)?.[0]
?.children.map((i) => (
<div
className={classnames(
'px-1.5 py-0.5 cursor-pointer rounded text-sm',
engine?.engine?._id === i._id
? 'bg-primary text-white'
: 'bg-white border',
)}
onClick={() => {
onEngineSelect(i);
}}
>
{i.name}
</div>
))}
</div>
<IconButton
onClick={() => {
onBtnClick(false);
}}
size="small"
>
<Close />
</IconButton>
</div>
</Card>
)}
</Popper>
<div className="w-full text-left mb-1 flex justify-start items-center overflow-x-auto">
{engineList.map((i, j) => (
<Chip
key={j}
className={classnames(
'mx-1',
i._id === engine?.engine?._id
? 'bg-primary text-white'
: 'bg-gray-100',
)}
size="small"
label={i.name}
onClick={(e) => onEngineSelect(i)}
></Chip>
))}
{engine.mode === 'custom' && (
<Chip
onClick={(e: any) => {
onBtnClick(!open);
}}
className={classnames('mx-1', 'bg-gray-100')}
size="small"
label={
<div className="text-sm flex gap-1 items-center">
<Settings className="text-base" />
</div>
}
/>
)}
</div>
</div>
);
}
Example #9
Source File: sugPopper.tsx From Search-Next with GNU General Public License v3.0 | 4 votes |
SugPopper: React.FC<SugPopperProps> = ({
open,
anchorEl,
wd,
code,
onSelect,
onKeySelect,
...props
}) => {
const [sugList, setSugList] = React.useState<SugList[]>([]);
const [engine, setEngine] = React.useState<SugEngine>({} as SugEngine);
const [selectedIndex, setSelectedIndex] = React.useState<number | null>(null);
const [refresh, setRefresh] = React.useState<boolean>(false);
const handRefresh = useDebounce(function () {
getSug();
}, 300);
const getSug = () => {
if (!wd) {
setSugList([]);
return;
}
setRefresh(true);
baiduSug(wd).then((res) => {
let data = res.data;
setSugList(data.sugs);
setEngine(data.engine);
setSelectedIndex(null);
setRefresh(false);
});
};
React.useEffect(() => {
handRefresh();
}, [wd]);
React.useEffect(() => {
const listLength = sugList.length;
let index = selectedIndex === null ? -1 : selectedIndex;
if (listLength > 0 && code) {
if (code === 'ArrowDown') index++;
if (code === 'ArrowUp') index--;
if (index >= listLength - 1) index = 0;
if (index < 0) index = listLength - 1;
const content = sugList[index]?.content;
if (content) {
onKeySelect(content);
setSelectedIndex(index);
}
}
}, [code]);
return (
<Popper
open={open && wd.length > 0}
anchorEl={anchorEl}
transition
placement="bottom"
container={anchorEl}
className="z-10 top-auto left-auto"
>
{({ TransitionProps }) => (
<Card
{...TransitionProps}
className="mt-1"
style={{ width: `${anchorEl?.clientWidth}px` }}
>
<Spin spinning={refresh} indicator={<LoadingOutlined spin />}>
{sugList.length ? (
<>
<List disablePadding>
{sugList.map((i, j) => (
<ListItem
button
key={j}
className={classNames({
'bg-gray-100': selectedIndex === j,
})}
onClick={() => {
setSelectedIndex(j);
onSelect(i.content);
}}
>
{i.content}
</ListItem>
))}
</List>
<p className="px-5 py-2 text-right">数据来源:{engine.name}</p>
</>
) : (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
)}
</Spin>
</Card>
)}
</Popper>
);
}