react-feather#Edit TypeScript Examples

The following examples show how to use react-feather#Edit. 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: EditorTreeFileItem.tsx    From gear-js with GNU General Public License v3.0 5 votes vote down vote up
EditorTreeFileItem = ({ item, isActive }: ItemProps) => {
  const { dispatch, onNodeClick, setCurrentFile } = useEditorTreeContext();
  const [isEditing, setEditing] = useState(false);

  function handleEdit() {
    setEditing(true);
  }

  function handleDelete() {
    // TODO: change to modal lib
    // eslint-disable-next-line no-alert
    if (window.confirm('Are you sure?') && dispatch) {
      dispatch({ type: FILE.DELETE, payload: { parentId: item.parentId, nodeId: item.id } });
      setCurrentFile(null);
    }
  }

  const handleCancel = () => {
    setEditing(false);
  };

  const handleSubmit = (name: string) => {
    if (dispatch) {
      dispatch({ type: FILE.UPDATE, payload: { parentId: item.parentId, nodeId: item.id, newName: name } });
      setEditing(false);
    }
  };

  const handleNodeClick = React.useCallback(() => {
    onNodeClick(item);
  }, [item, onNodeClick]);

  return (
    <div className={clsx('editor-tree__item', isActive && 'is-active')}>
      <div className="editor-tree__line" onClick={handleNodeClick} role="button" tabIndex={0} aria-hidden="true">
        <File size={12} />
        &nbsp;
        {isEditing ? (
          <EditorTreeInput onCancel={handleCancel} onSubmit={handleSubmit} value={item.name} type={EditorTypes.file} />
        ) : (
          <>{`${item.name}`}</>
        )}
      </div>
      <div className="tree-actions">
        &nbsp;
        <button className="tree-actions__btn" onClick={handleEdit} type="button">
          <Edit size={12} color="#fff" />
        </button>
        &nbsp;
        <button className="tree-actions__btn" onClick={handleDelete} type="button">
          <Trash size={12} color="#fff" />
        </button>
      </div>
    </div>
  );
}
Example #2
Source File: getMenuOrDrawerItems.tsx    From calories-in with MIT License 5 votes vote down vote up
EditStyled = chakra(Edit)
Example #3
Source File: getMenuOrDrawerItems.tsx    From calories-in with MIT License 5 votes vote down vote up
EditStyled = chakra(Edit)
Example #4
Source File: getMenuOrDrawerItems.tsx    From calories-in with MIT License 5 votes vote down vote up
EditStyled = chakra(Edit)
Example #5
Source File: ExpandableListItemInput.tsx    From bee-dashboard with BSD 3-Clause "New" or "Revised" License 4 votes vote down vote up
export default function ExpandableListItemKey({
  label,
  value,
  onConfirm,
  onChange,
  confirmLabel,
  confirmLabelDisabled,
  expandedOnly,
  helperText,
  placeholder,
  loading,
  mapperFn,
  locked,
}: Props): ReactElement | null {
  const classes = useStyles()
  const [open, setOpen] = useState(Boolean(expandedOnly))
  const [inputValue, setInputValue] = useState<string>(value || '')
  const toggleOpen = () => setOpen(!open)
  const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
    if (mapperFn) {
      e.target.value = mapperFn(e.target.value)
    }

    setInputValue(e.target.value)

    if (onChange) onChange(e.target.value)
  }

  return (
    <>
      <ListItem className={`${classes.header} ${open ? classes.headerOpen : ''}`}>
        <Grid container direction="column" justifyContent="space-between" alignItems="stretch">
          <Grid container direction="row" justifyContent="space-between" alignItems="center">
            {label && (
              <Typography variant="body1" className={classes.unselectableLabel}>
                {label}
              </Typography>
            )}
            <Typography variant="body2">
              <div>
                {!open && value}
                {!expandedOnly && !locked && (
                  <IconButton size="small" className={classes.copyValue}>
                    {open ? (
                      <Minus onClick={toggleOpen} strokeWidth={1} />
                    ) : (
                      <Edit onClick={toggleOpen} strokeWidth={1} />
                    )}
                  </IconButton>
                )}
              </div>
            </Typography>
          </Grid>
          <Collapse in={open} timeout="auto" unmountOnExit>
            <InputBase
              value={inputValue}
              placeholder={placeholder}
              onChange={handleChange}
              fullWidth
              className={classes.content}
              autoFocus
              hidden={locked}
            />
          </Collapse>
        </Grid>
      </ListItem>
      <Collapse in={open} timeout="auto" unmountOnExit>
        {helperText && <ExpandableListItemNote>{helperText}</ExpandableListItemNote>}
        <ExpandableListItemActions>
          <SwarmButton
            disabled={
              loading ||
              inputValue === value ||
              Boolean(confirmLabelDisabled) || // Disable if external validation is provided
              (inputValue === '' && value === undefined) // Disable if no initial value was not provided and the field is empty. The undefined check is improtant so that it is possible to submit with empty input in other cases
            }
            loading={loading}
            iconType={Search}
            onClick={() => {
              if (onConfirm) onConfirm(inputValue)
            }}
          >
            {confirmLabel || 'Save'}
          </SwarmButton>
          <SwarmButton
            disabled={loading || inputValue === value || inputValue === ''}
            iconType={X}
            onClick={() => setInputValue(value || '')}
            cancel
          >
            Cancel
          </SwarmButton>
        </ExpandableListItemActions>
      </Collapse>
    </>
  )
}
Example #6
Source File: EditorTreeFolderItem.tsx    From gear-js with GNU General Public License v3.0 4 votes vote down vote up
EditorTreeFolderItem = ({ item, children }: ItemProps) => {
  const { dispatch, setCurrentFile } = useEditorTreeContext();
  const [isEditing, setEditing] = useState(false);
  const [isOpen, setIsOpen] = useState(false);
  const [childrenCopy, setChildrenCopy] = useState<ReactNode[]>([]);

  useEffect(() => {
    setChildrenCopy([children]);
  }, [children]);

  const commitAddFile = (name: string) => {
    if (dispatch) {
      dispatch({ type: FILE.CREATE, payload: { parentId: item.id, newName: name } });
      setCurrentFile(null);
    }
  };
  const commitUpdateFolderName = (name: string) => {
    if (dispatch) {
      dispatch({ type: FOLDER.UPDATE, payload: { parentId: item.parentId, nodeId: item.id, newName: name } });
      setEditing(false);
    }
  };
  const commitAddFolder = (name: string) => {
    if (dispatch) {
      dispatch({ type: FOLDER.CREATE, payload: { parentId: item.id, newName: name } });
    }
  };
  const handleDelete = () => {
    // TODO: change to modal lib
    // eslint-disable-next-line no-alert
    if (window.confirm('Are you sure?') && dispatch) {
      dispatch({ type: FOLDER.DELETE, payload: { parentId: item.parentId, nodeId: item.id } });
      setCurrentFile(null);
    }
  };

  function handleClick() {
    setIsOpen(!isOpen);
  }

  function handleEdit(event: React.SyntheticEvent) {
    event.stopPropagation();
    setEditing(true);
  }

  function handleCancel() {
    setEditing(false);
    setChildrenCopy([children]);
  }

  function handleAddFile(event: React.SyntheticEvent) {
    event.stopPropagation();
    setIsOpen(true);
    /* eslint-disable react/jsx-no-bind */
    setChildrenCopy([
      ...childrenCopy,
      <EditorTreeInput
        type={EditorTypes.file}
        onSubmit={commitAddFile}
        onCancel={handleCancel}
        key={`editor-file-input-${item.id}`}
      />,
    ]);
  }

  function handleAddFolder(event: React.SyntheticEvent) {
    event.stopPropagation();
    setIsOpen(true);
    /* eslint-disable react/jsx-no-bind */
    setChildrenCopy([
      ...childrenCopy,
      <EditorTreeInput
        key={`editor-folder-input-${item.id}`}
        type={EditorTypes.folder}
        onSubmit={commitAddFolder}
        onCancel={handleCancel}
      />,
    ]);
  }

  /* eslint-disable react/jsx-no-bind */
  return (
    <div className={clsx('editor-tree__folder', isOpen && 'is-open')}>
      <div role="button" tabIndex={0} aria-hidden="true" className="editor-tree__item is-folder" onClick={handleClick}>
        <div className="editor-tree__line">
          <Folder size={12} />
          &nbsp;
          {isEditing ? (
            <EditorTreeInput
              type={EditorTypes.folder}
              onSubmit={commitUpdateFolderName}
              onCancel={handleCancel}
              value={item.name}
            />
          ) : (
            <>
              <span>{item.name}</span>
            </>
          )}
        </div>
        <div className="tree-actions">
          &nbsp;
          <button className="tree-actions__btn" onClick={handleAddFolder} type="button">
            <FolderPlus size={12} color="#fff" />
          </button>
          &nbsp;
          <button className="tree-actions__btn" onClick={handleAddFile} type="button">
            <FilePlus size={12} color="#fff" />
          </button>
          &nbsp;
          <button className="tree-actions__btn" onClick={handleEdit} type="button">
            <Edit size={12} color="#fff" />
          </button>
          &nbsp;
          <button className="tree-actions__btn" onClick={handleDelete} type="button">
            <Trash size={12} color="#fff" />
          </button>
        </div>
      </div>
      <div className="editor-tree__folder-items">{childrenCopy}</div>
    </div>
  );
}
Example #7
Source File: CurrencySearch.tsx    From limit-orders-lib with GNU General Public License v3.0 4 votes vote down vote up
export function CurrencySearch({
  selectedCurrency,
  onCurrencySelect,
  otherSelectedCurrency,
  showCommonBases,
  onDismiss,
  isOpen,
  showManageView,
  showImportView,
  setImportToken,
}: CurrencySearchProps) {
  const { chainId } = useWeb3();
  const theme = useTheme();

  // refs for fixed size lists
  const fixedList = useRef<FixedSizeList>();

  const [searchQuery, setSearchQuery] = useState<string>("");
  const debouncedQuery = useDebounce(searchQuery, 200);

  const [invertSearchOrder] = useState<boolean>(false);

  const allTokens = useAllTokens();

  // if they input an address, use it
  const isAddressSearch = isAddress(debouncedQuery);

  const searchToken = useToken(debouncedQuery);

  const searchTokenIsAdded = useIsUserAddedToken(searchToken);

  const tokenComparator = useTokenComparator(invertSearchOrder);

  const filteredTokens: Token[] = useMemo(() => {
    return filterTokens(Object.values(allTokens), debouncedQuery);
  }, [allTokens, debouncedQuery]);

  const sortedTokens: Token[] = useMemo(() => {
    return filteredTokens.sort(tokenComparator);
  }, [filteredTokens, tokenComparator]);

  const filteredSortedTokens = useSortedTokensByQuery(
    sortedTokens,
    debouncedQuery
  );

  const ether = useCurrency("NATIVE");

  const filteredSortedTokensWithETH: Currency[] = useMemo(() => {
    const s = debouncedQuery.toLowerCase().trim();
    if (s === "" || s === "e" || s === "et" || s === "eth") {
      return ether ? [ether, ...filteredSortedTokens] : filteredSortedTokens;
    }
    return filteredSortedTokens;
  }, [debouncedQuery, ether, filteredSortedTokens]);

  const handleCurrencySelect = useCallback(
    (currency: Currency) => {
      onCurrencySelect(currency);
      onDismiss();
    },
    [onDismiss, onCurrencySelect]
  );

  // clear the input on open
  useEffect(() => {
    if (isOpen) setSearchQuery("");
  }, [isOpen]);

  // manage focus on modal show
  const inputRef = useRef<HTMLInputElement>();
  const handleInput = useCallback((event) => {
    const input = event.target.value;
    const checksummedInput = isAddress(input);
    setSearchQuery(checksummedInput || input);
    fixedList.current?.scrollTo(0);
  }, []);

  const handleEnter = useCallback(
    (e: KeyboardEvent<HTMLInputElement>) => {
      if (e.key === "Enter") {
        const s = debouncedQuery.toLowerCase().trim();
        if (s === "eth" && ether) {
          handleCurrencySelect(ether);
        } else if (filteredSortedTokensWithETH.length > 0) {
          if (
            filteredSortedTokensWithETH[0].symbol?.toLowerCase() ===
              debouncedQuery.trim().toLowerCase() ||
            filteredSortedTokensWithETH.length === 1
          ) {
            handleCurrencySelect(filteredSortedTokensWithETH[0]);
          }
        }
      }
    },
    [debouncedQuery, ether, filteredSortedTokensWithETH, handleCurrencySelect]
  );

  // menu ui
  const [open, toggle] = useToggle(false);
  const node = useRef<HTMLDivElement>();
  useOnClickOutside(node, open ? toggle : undefined);

  // if no results on main list, show option to expand into inactive
  const filteredInactiveTokens = useSearchInactiveTokenLists(
    filteredTokens.length === 0 ||
      (debouncedQuery.length > 2 && !isAddressSearch)
      ? debouncedQuery
      : undefined
  );

  return (
    <ContentWrapper>
      <PaddedColumn gap="16px">
        <RowBetween>
          <Text fontWeight={500} fontSize={16}>
            Select a token
          </Text>
          <CloseIcon onClick={onDismiss} />
        </RowBetween>
        <Row>
          <SearchInput
            type="text"
            id="token-search-input"
            placeholder={"Search name or paste address"}
            autoComplete="off"
            value={searchQuery}
            ref={inputRef as RefObject<HTMLInputElement>}
            onChange={handleInput}
            onKeyDown={handleEnter}
          />
        </Row>
        {showCommonBases && (
          <CommonBases
            chainId={chainId}
            onSelect={handleCurrencySelect}
            selectedCurrency={selectedCurrency}
          />
        )}
      </PaddedColumn>
      <Separator />
      {searchToken && !searchTokenIsAdded ? (
        <Column style={{ padding: "20px 0", height: "100%" }}>
          <ImportRow
            token={searchToken}
            showImportView={showImportView}
            setImportToken={setImportToken}
          />
        </Column>
      ) : filteredSortedTokens?.length > 0 ||
        filteredInactiveTokens?.length > 0 ? (
        <div style={{ flex: "1" }}>
          <AutoSizer disableWidth>
            {({ height }) => (
              <CurrencyList
                height={height}
                currencies={filteredSortedTokensWithETH}
                otherListTokens={filteredInactiveTokens}
                onCurrencySelect={handleCurrencySelect}
                otherCurrency={otherSelectedCurrency}
                selectedCurrency={selectedCurrency}
                fixedListRef={fixedList}
                showImportView={showImportView}
                setImportToken={setImportToken}
              />
            )}
          </AutoSizer>
        </div>
      ) : (
        <Column style={{ padding: "20px", height: "100%" }}>
          <TYPE.main color={theme.text3} textAlign="center" mb="20px">
            No results found.
          </TYPE.main>
        </Column>
      )}
      <Footer>
        <Row justify="center">
          <ButtonText
            onClick={showManageView}
            color={theme.blue1}
            className="list-token-manage-button"
          >
            <RowFixed>
              <IconWrapper size="16px" marginRight="6px">
                <Edit />
              </IconWrapper>
              <TYPE.main color={theme.blue1}>Manage Token Lists</TYPE.main>
            </RowFixed>
          </ButtonText>
        </Row>
      </Footer>
    </ContentWrapper>
  );
}
Example #8
Source File: CurrencySearch.tsx    From forward.swaps with GNU General Public License v3.0 4 votes vote down vote up
export function CurrencySearch({
  selectedCurrency,
  onCurrencySelect,
  otherSelectedCurrency,
  showCommonBases,
  onDismiss,
  isOpen,
  showManageView,
  showImportView,
  setImportToken
}: CurrencySearchProps) {
  const { t } = useTranslation()
  const { chainId } = useActiveWeb3React()
  const theme = useTheme()

  // refs for fixed size lists
  const fixedList = useRef<FixedSizeList>()

  const [searchQuery, setSearchQuery] = useState<string>('')
  const [invertSearchOrder] = useState<boolean>(false)

  const allTokens = useAllTokens()
  // const inactiveTokens: Token[] | undefined = useFoundOnInactiveList(searchQuery)

  // if they input an address, use it
  const isAddressSearch = isAddress(searchQuery)
  const searchToken = useToken(searchQuery)
  const searchTokenIsAdded = useIsUserAddedToken(searchToken)

  useEffect(() => {
    if (isAddressSearch) {
      ReactGA.event({
        category: 'Currency Select',
        action: 'Search by address',
        label: isAddressSearch
      })
    }
  }, [isAddressSearch])

  const showETH: boolean = useMemo(() => {
    const s = searchQuery.toLowerCase().trim()
    return s === '' || s === 'e' || s === 'et' || s === 'eth'
  }, [searchQuery])

  const tokenComparator = useTokenComparator(invertSearchOrder)

  const filteredTokens: Token[] = useMemo(() => {
    return filterTokens(Object.values(allTokens), searchQuery)
  }, [allTokens, searchQuery])

  const filteredSortedTokens: Token[] = useMemo(() => {
    const sorted = filteredTokens.sort(tokenComparator)
    const symbolMatch = searchQuery
      .toLowerCase()
      .split(/\s+/)
      .filter(s => s.length > 0)

    if (symbolMatch.length > 1) {
      return sorted
    }

    return [
      // sort any exact symbol matches first
      ...sorted.filter(token => token.symbol?.toLowerCase() === symbolMatch[0]),

      // sort by tokens whos symbols start with search substrng
      ...sorted.filter(
        token =>
          token.symbol?.toLowerCase().startsWith(searchQuery.toLowerCase().trim()) &&
          token.symbol?.toLowerCase() !== symbolMatch[0]
      ),

      // rest that dont match upove
      ...sorted.filter(
        token =>
          !token.symbol?.toLowerCase().startsWith(searchQuery.toLowerCase().trim()) &&
          token.symbol?.toLowerCase() !== symbolMatch[0]
      )
    ]
  }, [filteredTokens, searchQuery, tokenComparator])

  const handleCurrencySelect = useCallback(
    (currency: Currency) => {
      onCurrencySelect(currency)
      onDismiss()
    },
    [onDismiss, onCurrencySelect]
  )

  // clear the input on open
  useEffect(() => {
    if (isOpen) setSearchQuery('')
  }, [isOpen])

  // manage focus on modal show
  const inputRef = useRef<HTMLInputElement>()
  const handleInput = useCallback(event => {
    const input = event.target.value
    const checksummedInput = isAddress(input)
    setSearchQuery(checksummedInput || input)
    fixedList.current?.scrollTo(0)
  }, [])

  const handleEnter = useCallback(
    (e: KeyboardEvent<HTMLInputElement>) => {
      if (e.key === 'Enter') {
        const s = searchQuery.toLowerCase().trim()
        if (s === 'eth') {
          handleCurrencySelect(ETHER)
        } else if (filteredSortedTokens.length > 0) {
          if (
            filteredSortedTokens[0].symbol?.toLowerCase() === searchQuery.trim().toLowerCase() ||
            filteredSortedTokens.length === 1
          ) {
            handleCurrencySelect(filteredSortedTokens[0])
          }
        }
      }
    },
    [filteredSortedTokens, handleCurrencySelect, searchQuery]
  )

  // menu ui
  const [open, toggle] = useToggle(false)
  const node = useRef<HTMLDivElement>()
  useOnClickOutside(node, open ? toggle : undefined)

  // if no results on main list, show option to expand into inactive
  const [showExpanded, setShowExpanded] = useState(false)
  const inactiveTokens = useFoundOnInactiveList(searchQuery)

  // reset expanded results on query reset
  useEffect(() => {
    if (searchQuery === '') {
      setShowExpanded(false)
    }
  }, [setShowExpanded, searchQuery])

  return (
    <ContentWrapper>
      <PaddedColumn gap="16px">
        <RowBetween>
          <Text fontWeight={500} fontSize={16}>
            Select a token
          </Text>
          <CloseIcon onClick={onDismiss} />
        </RowBetween>
        <Row>
          <SearchInput
            type="text"
            id="token-search-input"
            placeholder={t('tokenSearchPlaceholder')}
            autoComplete="off"
            value={searchQuery}
            ref={inputRef as RefObject<HTMLInputElement>}
            onChange={handleInput}
            onKeyDown={handleEnter}
          />
        </Row>
        {showCommonBases && (
          <CommonBases chainId={chainId} onSelect={handleCurrencySelect} selectedCurrency={selectedCurrency} />
        )}
      </PaddedColumn>
      <Separator />
      {searchToken && !searchTokenIsAdded ? (
        <Column style={{ padding: '20px 0', height: '100%' }}>
          <ImportRow token={searchToken} showImportView={showImportView} setImportToken={setImportToken} />
        </Column>
      ) : filteredSortedTokens?.length > 0 || (showExpanded && inactiveTokens && inactiveTokens.length > 0) ? (
        <div style={{ flex: '1' }}>
          <AutoSizer disableWidth>
            {({ height }) => (
              <CurrencyList
                height={height}
                showETH={showETH}
                currencies={
                  showExpanded && inactiveTokens ? filteredSortedTokens.concat(inactiveTokens) : filteredSortedTokens
                }
                onCurrencySelect={handleCurrencySelect}
                otherCurrency={otherSelectedCurrency}
                selectedCurrency={selectedCurrency}
                fixedListRef={fixedList}
                showImportView={showImportView}
                setImportToken={setImportToken}
              />
            )}
          </AutoSizer>
        </div>
      ) : (
        <Column style={{ padding: '20px', height: '100%' }}>
          <TYPE.main color={theme.text3} textAlign="center" mb="20px">
            No results found in active lists.
          </TYPE.main>
          {inactiveTokens &&
            inactiveTokens.length > 0 &&
            !(searchToken && !searchTokenIsAdded) &&
            searchQuery.length > 1 &&
            filteredSortedTokens?.length === 0 && (
              // expand button in line with no results
              <Row align="center" width="100%" justify="center">
                <ButtonLight
                  width="fit-content"
                  borderRadius="12px"
                  padding="8px 12px"
                  onClick={() => setShowExpanded(!showExpanded)}
                >
                  {!showExpanded
                    ? `Show ${inactiveTokens.length} more inactive ${inactiveTokens.length === 1 ? 'token' : 'tokens'}`
                    : 'Hide expanded search'}
                </ButtonLight>
              </Row>
            )}
        </Column>
      )}

      {inactiveTokens &&
        inactiveTokens.length > 0 &&
        !(searchToken && !searchTokenIsAdded) &&
        (searchQuery.length > 1 || showExpanded) &&
        (filteredSortedTokens?.length !== 0 || showExpanded) && (
          // button fixed to bottom
          <Row align="center" width="100%" justify="center" style={{ position: 'absolute', bottom: '80px', left: 0 }}>
            <ButtonLight
              width="fit-content"
              borderRadius="12px"
              padding="8px 12px"
              onClick={() => setShowExpanded(!showExpanded)}
            >
              {!showExpanded
                ? `Show ${inactiveTokens.length} more inactive ${inactiveTokens.length === 1 ? 'token' : 'tokens'}`
                : 'Hide expanded search'}
            </ButtonLight>
          </Row>
        )}
      <Footer>
        <Row justify="center">
          <ButtonText onClick={showManageView} color={theme.blue1} className="list-token-manage-button">
            <RowFixed>
              <IconWrapper size="16px" marginRight="6px">
                <Edit />
              </IconWrapper>
              <TYPE.main color={theme.blue1}>Manage</TYPE.main>
            </RowFixed>
          </ButtonText>
        </Row>
      </Footer>
    </ContentWrapper>
  )
}