blob: dc294d04919b3077ac8f7056a0c6b73a4746488b (
plain) (
tree)
|
|
import { getContext, setContext } from 'svelte';
import { SvelteSet } from 'svelte/reactivity';
import { range } from '../Utils';
interface Selectable {
id: number;
}
export function initSelectionContext<T extends Selectable>(
typename: string,
toName: (item: T) => string,
selectable?: (item: T) => boolean
) {
return setContext('selection', new ItemSelection(typename, toName, selectable));
}
export function getSelectionContext<T extends Selectable>() {
return getContext<ItemSelection<T>>('selection');
}
export class ItemSelection<T extends Selectable> {
active = $state(false);
view: T[] = $state([]);
#ids = $state(new SvelteSet<number>());
#masked = $derived(new SvelteSet([...this.#ids].filter((i) => this.#indexOf(i) >= 0)));
typename: string;
#toName: (item: T) => string;
selectable: (item: T) => boolean;
constructor(
typename: string,
toName: (item: T) => string,
selectable: (item: T) => boolean = () => true
) {
this.typename = typename;
this.#toName = toName;
this.selectable = selectable;
}
#indexOf = (id: number) => this.view.findIndex((v) => v.id === id);
update = (index: number, shift: boolean) => {
const id = this.view[index].id;
const selectableRange = (first: number, last: number) =>
range(first, last)
.filter((i) => this.selectable(this.view[i]))
.map((i) => this.view[i].id);
if (shift) {
const indices = this.indices;
const first = indices.at(0);
const last = indices.at(-1);
if (first === undefined || last === undefined) {
this.#ids.add(id);
} else if (index === first || index === last) {
this.#ids.clear();
} else if (index > last) {
this.#ids = new SvelteSet([...this.#ids, ...selectableRange(last, index)]);
} else if (index < last) {
this.#ids = new SvelteSet([...this.#ids, ...selectableRange(index, last)]);
}
} else {
if (this.#ids.has(id)) {
this.#ids.delete(id);
} else {
this.#ids.add(id);
}
}
};
toggle = () => {
this.active = !this.active;
if (!this.active) {
this.none();
}
};
all = () => {
this.#ids = new SvelteSet(this.view.filter(this.selectable).map((i) => i.id));
};
none = () => {
this.#ids.clear();
this.#masked.clear();
};
clear = () => {
this.active = false;
this.none();
};
contains(id: number) {
return this.#masked.has(id);
}
get ids() {
return [...this.#masked];
}
get size() {
return this.#masked.size;
}
get indices() {
return [...this.#ids].map(this.#indexOf).filter((i) => i >= 0);
}
get items() {
return this.indices.map((i) => this.view[i]);
}
get names() {
return this.items.map(this.#toName);
}
}
|