feat(charts): manage investigation charts in a scrolling workspace
This commit is contained in:
@@ -1,23 +1,58 @@
|
||||
let order: string[] = [];
|
||||
const listeners = new Set<() => void>();
|
||||
let host: HTMLDivElement | undefined;
|
||||
export type FloatingWindowEntry = { id: string; title: string; focus: () => void; close: () => void };
|
||||
const registered = new Map<string, {
|
||||
entry: FloatingWindowEntry;
|
||||
actions: Pick<FloatingWindowEntry, "focus" | "close">;
|
||||
}>();
|
||||
let entries: readonly FloatingWindowEntry[] = [];
|
||||
|
||||
function publishWindows() {
|
||||
entries = order.flatMap((id) => {
|
||||
const record = registered.get(id);
|
||||
return record ? [record.entry] : [];
|
||||
});
|
||||
listeners.forEach((listener) => listener());
|
||||
}
|
||||
|
||||
export const floatingWindowOrder = () => order;
|
||||
/** Stable between changes, ordered from the back window to the foreground. */
|
||||
export const floatingWindowEntries = () => entries;
|
||||
export const subscribeFloatingWindows = (listener: () => void) => {
|
||||
listeners.add(listener);
|
||||
return () => { listeners.delete(listener); };
|
||||
};
|
||||
|
||||
export function registerFloatingWindow(entry: FloatingWindowEntry) {
|
||||
const previous = registered.get(entry.id);
|
||||
if (previous) {
|
||||
// Callbacks can change without invalidating the subscribed presentation.
|
||||
previous.actions.focus = entry.focus;
|
||||
previous.actions.close = entry.close;
|
||||
if (previous.entry.title === entry.title) return;
|
||||
previous.entry = { ...previous.entry, title: entry.title };
|
||||
} else {
|
||||
const actions = { focus: entry.focus, close: entry.close };
|
||||
registered.set(entry.id, { actions, entry: { id: entry.id, title: entry.title,
|
||||
focus: () => { if (registered.get(entry.id)?.actions === actions) actions.focus(); },
|
||||
close: () => { if (registered.get(entry.id)?.actions === actions) actions.close(); } } });
|
||||
if (!order.includes(entry.id)) order = [...order, entry.id];
|
||||
}
|
||||
publishWindows();
|
||||
}
|
||||
|
||||
export function raiseFloatingWindow(id: string) {
|
||||
if (order.at(-1) === id) return;
|
||||
order = [...order.filter((item) => item !== id), id];
|
||||
listeners.forEach((listener) => listener());
|
||||
publishWindows();
|
||||
}
|
||||
|
||||
export function removeFloatingWindow(id: string) {
|
||||
if (!order.includes(id)) return;
|
||||
if (!order.includes(id) && !registered.has(id)) return;
|
||||
registered.delete(id);
|
||||
order = order.filter((item) => item !== id);
|
||||
listeners.forEach((listener) => listener());
|
||||
publishWindows();
|
||||
}
|
||||
|
||||
export function floatingWindowHost() {
|
||||
|
||||
Reference in New Issue
Block a user