import React, { useEffect, useImperativeHandle, useState } from "react"; import { useCombobox } from "downshift"; import styled from "styled-components"; import { defaultBorderRadius, lightGray, secondaryDark, vscBackground, vscForeground, } from "."; import CodeBlock from "./CodeBlock"; import PillButton from "./PillButton"; import HeaderButtonWithText from "./HeaderButtonWithText"; import { DocumentPlus } from "@styled-icons/heroicons-outline"; import { HighlightedRangeContext } from "../../../schema/FullState"; import { postVscMessage } from "../vscode"; import { getMetaKeyLabel } from "../util"; // #region styled components const mainInputFontSize = 13; const EmptyPillDiv = styled.div` padding: 8px; border-radius: ${defaultBorderRadius}; border: 1px dashed ${lightGray}; color: ${lightGray}; background-color: ${vscBackground}; overflow: hidden; display: flex; align-items: center; text-align: center; cursor: pointer; font-size: 13px; &:hover { background-color: ${lightGray}; color: ${vscBackground}; } `; const MainTextInput = styled.textarea` resize: none; padding: 8px; font-size: ${mainInputFontSize}px; font-family: inherit; border-radius: ${defaultBorderRadius}; margin: 8px auto; height: auto; width: 100%; background-color: ${secondaryDark}; color: ${vscForeground}; z-index: 1; border: 1px solid transparent; &:focus { outline: 1px solid ${lightGray}; border: 1px solid transparent; } `; const UlMaxHeight = 300; const Ul = styled.ul<{ hidden: boolean; showAbove: boolean; ulHeightPixels: number; }>` ${(props) => props.showAbove ? `transform: translateY(-${props.ulHeightPixels + 8}px);` : `transform: translateY(${2 * mainInputFontSize}px);`} position: absolute; background: ${vscBackground}; background-color: ${secondaryDark}; color: ${vscForeground}; max-height: ${UlMaxHeight}px; width: calc(100% - 16px); overflow-y: scroll; overflow-x: hidden; padding: 0; ${({ hidden }) => hidden && "display: none;"} border-radius: ${defaultBorderRadius}; outline: 0.5px solid gray; z-index: 2; // Get rid of scrollbar and its padding scrollbar-width: none; -ms-overflow-style: none; &::-webkit-scrollbar { width: 0px; background: transparent; /* make scrollbar transparent */ } `; const Li = styled.li<{ highlighted: boolean; selected: boolean; isLastItem: boolean; }>` background-color: ${secondaryDark}; ${({ highlighted }) => highlighted && "background: #ff000066;"} ${({ selected }) => selected && "font-weight: bold;"} padding: 0.5rem 0.75rem; display: flex; flex-direction: column; ${({ isLastItem }) => isLastItem && "border-bottom: 1px solid gray;"} border-top: 1px solid gray; cursor: pointer; `; // #endregion interface ComboBoxProps { items: { name: string; description: string }[]; onInputValueChange: (inputValue: string) => void; disabled?: boolean; onEnter: (e: React.KeyboardEvent) => void; highlightedCodeSections: HighlightedRangeContext[]; deleteContextItems: (indices: number[]) => void; onTogglePin: () => void; onToggleAddContext: () => void; addingHighlightedCode: boolean; } const ComboBox = React.forwardRef((props: ComboBoxProps, ref) => { const [history, setHistory] = React.useState([]); // The position of the current command you are typing now, so the one that will be appended to history once you press enter const [positionInHistory, setPositionInHistory] = React.useState(0); const [items, setItems] = React.useState(props.items); const [highlightedCodeSections, setHighlightedCodeSections] = React.useState( props.highlightedCodeSections || [] ); const inputRef = React.useRef(null); useEffect(() => { setHighlightedCodeSections(props.highlightedCodeSections || []); }, [props.highlightedCodeSections]); const { getInputProps, ...downshiftProps } = useCombobox({ onInputValueChange({ inputValue }) { if (!inputValue) return; props.onInputValueChange(inputValue); setItems( props.items.filter((item) => item.name.toLowerCase().startsWith(inputValue.toLowerCase()) ) ); }, items, itemToString(item) { return item ? item.name : ""; }, }); useImperativeHandle(ref, () => downshiftProps, [downshiftProps]); const [metaKeyPressed, setMetaKeyPressed] = useState(false); const [focused, setFocused] = useState(false); useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Meta") { setMetaKeyPressed(true); } }; const handleKeyUp = (e: KeyboardEvent) => { if (e.key === "Meta") { setMetaKeyPressed(false); } }; window.addEventListener("keydown", handleKeyDown); window.addEventListener("keyup", handleKeyUp); return () => { window.removeEventListener("keydown", handleKeyDown); window.removeEventListener("keyup", handleKeyUp); }; }); useEffect(() => { if (!inputRef.current) { return; } inputRef.current.focus(); const handler = (event: any) => { if (event.data.type === "focusContinueInput") { inputRef.current!.focus(); } }; window.addEventListener("message", handler); return () => { window.removeEventListener("message", handler); }; }, [inputRef.current]); const divRef = React.useRef(null); const ulRef = React.useRef(null); const showAbove = () => { return (divRef.current?.getBoundingClientRect().top || 0) > UlMaxHeight; }; return ( <>
{/* {highlightedCodeSections.length > 1 && ( <> { props.deleteContextItems( highlightedCodeSections.map((_, idx) => idx) ); }} > )} */} {highlightedCodeSections.map((section, idx) => ( 4000 && section.editing ? "Editing such a large range may be slow" : undefined } onlyShowDelete={ highlightedCodeSections.length <= 1 || section.editing } editing={section.editing} pinned={section.pinned} index={idx} key={`${section.display_name}${idx}`} title={`${section.display_name} (${ section.range.range.start.line + 1 }-${section.range.range.end.line + 1})`} onDelete={() => { if (props.deleteContextItems) { props.deleteContextItems([idx]); } setHighlightedCodeSections((prev) => { const newSections = [...prev]; newSections.splice(idx, 1); return newSections; }); }} /> ))} {props.highlightedCodeSections.length > 0 && (props.addingHighlightedCode ? ( { props.onToggleAddContext(); }} > Highlight code section ) : ( { props.onToggleAddContext(); }} > ))}
{highlightedCodeSections.length === 0 && (downshiftProps.inputValue?.startsWith("/edit") || (focused && metaKeyPressed && downshiftProps.inputValue?.length > 0)) && (
Inserting at cursor
)} ); }); export default ComboBox;