import { NodeViewProps, NodeViewWrapper, ReactRenderer } from "@tiptap/react"; import { Popover, PopoverContent, PopoverTrigger, } from "@unsend/ui/src/popover"; import { cn } from "@unsend/ui/lib/utils"; import { Input } from "@unsend/ui/src/input"; import { Button } from "@unsend/ui/src/button"; import { forwardRef, useEffect, useImperativeHandle, useState } from "react"; import { SuggestionOptions } from "@tiptap/suggestion"; import tippy, { GetReferenceClientRect } from "tippy.js"; import { CheckIcon, TriangleAlert } from "lucide-react"; export interface VariableOptions { name: string; fallback: string; } export const VariableList = forwardRef((props: any, ref) => { const [selectedIndex, setSelectedIndex] = useState(0); const selectItem = (index: number) => { const item = props.items[index]; console.log("item: ", item); if (item) { props.command({ id: item, name: item, fallback: "" }); } }; useEffect(() => setSelectedIndex(0), [props.items]); useImperativeHandle(ref, () => ({ onKeyDown: ({ event }: { event: KeyboardEvent }) => { if (event.key === "ArrowUp") { setSelectedIndex( (selectedIndex + props.items.length - 1) % props.items.length ); return true; } if (event.key === "ArrowDown") { setSelectedIndex((selectedIndex + 1) % props.items.length); return true; } if (event.key === "Enter") { selectItem(selectedIndex); return true; } return false; }, })); return (
{props?.items?.length ? ( props?.items?.map((item: string, index: number) => ( )) ) : ( )}
); }); VariableList.displayName = "VariableList"; export function getVariableSuggestions( variables: Array = [] ): Omit { return { items: ({ query }) => { return variables .concat(query.length > 0 ? [query] : []) .filter((item) => item.toLowerCase().startsWith(query.toLowerCase())) .slice(0, 5); }, render: () => { let component: ReactRenderer; let popup: InstanceType | null = null; return { onStart: (props) => { component = new ReactRenderer(VariableList, { props, editor: props.editor, }); if (!props.clientRect) { return; } popup = tippy("body", { getReferenceClientRect: props.clientRect as GetReferenceClientRect, appendTo: () => document.body, content: component.element, showOnCreate: true, interactive: true, trigger: "manual", placement: "bottom-start", }); }, onUpdate(props) { component.updateProps(props); if (!props.clientRect) { return; } popup?.[0]?.setProps({ getReferenceClientRect: props.clientRect as GetReferenceClientRect, }); }, onKeyDown(props) { if (props.event.key === "Escape") { popup?.[0].hide(); return true; } return component.ref?.onKeyDown(props); }, onExit() { if (!popup || !popup?.[0] || !component) { return; } popup?.[0].destroy(); component.destroy(); }, }; }, }; } export function VariableComponent(props: NodeViewProps) { const { name, fallback } = props.node.attrs as VariableOptions; const [fallbackValue, setFallbackValue] = useState(fallback); const { getPos, editor } = props; console.log(props.selected); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); props.updateAttributes({ fallback: fallbackValue, }); }; return ( e.preventDefault()} onCloseAutoFocus={(e) => e.preventDefault()} >
{ setFallbackValue(e.target.value); }} autoFocus />
Fallback value will be used if the variable value is empty.
); }