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 component’s 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|