Files
olivv-cs-ComfyUI-FunPack/app/ui/shell/selection.js
T

40 lines
2.0 KiB
JavaScript

// Which clips are selected: a set, one of them in focus (what the inspector shows), and an anchor for shift-ranges.
// Click picks one, cmd/ctrl toggles, shift extends from the anchor. Pure; the project says which scene is focused.
export function createSelection({ project }) {
let ids = [];
let anchor = null;
const heard = new Set();
const say = () => heard.forEach((fn) => { try { fn(); } catch { /* one listener must not stop the rest */ } });
const live = () => new Set(project.scenes.map((s) => s.id));
const heal = () => { const have = live(); ids = ids.filter((id) => have.has(id)); if (anchor && !have.has(anchor)) anchor = project.selectedId; };
return {
get ids() { heal(); return ids.length ? [...ids] : (project.selectedId ? [project.selectedId] : []); },
has(id) { return this.ids.includes(id); },
get focus() { return project.selectedId; },
on: (fn) => { heard.add(fn); return () => heard.delete(fn); },
/** `order`: scene ids as the timeline shows them, which is what a range means. */
pick(id, { additive = false, range = false } = {}, order = project.scenes.map((s) => s.id)) {
if (!project.scenes.some((s) => s.id === id)) return;
const was = this.ids;
if (range && anchor) {
const a = order.indexOf(anchor), b = order.indexOf(id);
ids = a < 0 || b < 0 ? [id] : order.slice(Math.min(a, b), Math.max(a, b) + 1);
} else if (additive) {
ids = was.includes(id) ? was.filter((x) => x !== id) : [...was, id];
anchor = ids.includes(id) ? id : ids[ids.length - 1] || null; // after un-picking, the last one still picked
} else {
ids = [id];
anchor = id;
}
const focus = ids.includes(id) ? id : ids[ids.length - 1] || null;
if (focus) project.select(focus);
say();
},
clear() { ids = []; anchor = null; say(); },
/** The project was replaced or restructured: drop what no longer exists. */
heal() { heal(); say(); },
};
}