@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 vote down vote up
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 vote down vote up
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 vote down vote up
StyledAutocompletePopper = styled(Popper)(({ theme }) => ({
    minWidth: 200,
}))
Example #4
Source File: AccountSelect.tsx    From abrechnung with GNU Affero General Public License v3.0 5 votes vote down vote up
StyledAutocompletePopper = styled(Popper)(({ theme }) => ({
    minWidth: 200,
}))
Example #5
Source File: Autocomplete.tsx    From Cromwell with MIT License 4 votes vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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 vote down vote up
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>
  );
}