open-webui/src/lib/components/common/RichTextInput/suggestions.ts

86 lines
2.2 KiB
TypeScript
Raw Normal View History

2025-09-12 16:31:57 +00:00
import tippy from 'tippy.js';
export function getSuggestionRenderer(Component: any, ComponentProps = {}) {
return function suggestionRenderer() {
let component = null;
let container: HTMLDivElement | null = null;
2025-09-12 20:24:42 +00:00
2025-09-12 16:31:57 +00:00
let popup: TippyInstance | null = null;
2025-09-12 20:24:42 +00:00
let refEl: HTMLDivElement | null = null; // dummy reference
2025-09-12 16:31:57 +00:00
return {
onStart: (props: any) => {
container = document.createElement('div');
container.className = 'suggestion-list-container';
document.body.appendChild(container);
// mount Svelte component
component = new Component({
target: container,
props: {
char: props?.text,
command: (item) => {
props.command({ id: item.id, label: item.label });
},
...ComponentProps
},
context: new Map<string, any>([['i18n', ComponentProps?.i18n]])
});
2025-09-12 20:24:42 +00:00
// Create a tiny reference element so outside taps are truly "outside"
refEl = document.createElement('div');
Object.assign(refEl.style, {
position: 'fixed',
left: '0px',
top: '0px',
width: '0px',
height: '0px'
});
document.body.appendChild(refEl);
popup = tippy(refEl, {
2025-09-12 16:31:57 +00:00
getReferenceClientRect: props.clientRect as any,
appendTo: () => document.body,
2025-09-12 20:24:42 +00:00
content: container,
2025-09-12 16:31:57 +00:00
interactive: true,
trigger: 'manual',
theme: 'transparent',
placement: 'top-start',
offset: [-10, -2],
arrow: false
});
popup?.show();
},
onUpdate: (props: any) => {
if (!component) return;
component.$set({ query: props.query });
if (props.clientRect && popup) {
popup.setProps({ getReferenceClientRect: props.clientRect as any });
}
},
onKeyDown: (props: any) => {
// forward to the Svelte components handler
// (expose this from component as `export function onKeyDown(evt)`)
// @ts-ignore
return component?._onKeyDown?.(props.event) ?? false;
},
onExit: () => {
popup?.destroy();
popup = null;
component?.$destroy();
component = null;
if (container?.parentNode) container.parentNode.removeChild(container);
container = null;
2025-09-12 20:30:00 +00:00
if (refEl?.parentNode) refEl.parentNode.removeChild(refEl);
refEl = null;
2025-09-12 16:31:57 +00:00
}
};
};
}