blob: 0ea85cca079d9a04cf817d7c1a2bb1c50eda3561 (
plain) (
tree)
|
|
import { getContext, hasContext, setContext } from 'svelte';
import { writable, type Writable } from 'svelte/store';
import { range } from './Utils';
interface Item {
id: number;
}
export const hasSelectionContext = () => hasContext('selection');
export function getSelectionContext<T extends Item>() {
return getContext<Writable<ItemSelection<T>>>('selection');
}
export function initSelectionContext<T extends Item>(
typename?: string,
toName?: (item: T) => string
) {
return setContext<Writable<ItemSelection<T>>>(
'selection',
writable(new ItemSelection(typename, toName))
);
}
export class ItemSelection<T extends Item> {
active = false;
typename: string;
#toName: (item: T) => string;
#view: T[] = [];
selectable: (item: T) => boolean = () => true;
#ids = new Set<number>();
#masked = new Set<number>();
constructor(typename?: string, toName?: (item: T) => string) {
this.typename = typename ?? 'unknown';
this.#toName = toName ?? (() => 'unknown');
}
set view(view: T[]) {
this.#view = view;
this.#updateMasked();
}
#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 Set([...this.#ids, ...selectableRange(last, index)]);
} else if (index < last) {
this.#ids = new Set([...this.#ids, ...selectableRange(index, last)]);
}
} else {
if (this.#ids.has(id)) {
this.#ids.delete(id);
} else {
this.#ids.add(id);
}
}
this.#updateMasked();
return this;
}
toggle() {
this.active = !this.active;
if (!this.active) {
return this.none();
}
return this;
}
all() {
this.#ids = new Set(this.#view.filter(this.selectable).map((i) => i.id));
this.#updateMasked();
return this;
}
none() {
this.#ids.clear();
this.#masked.clear();
return this;
}
clear() {
this.active = false;
return this.none();
}
contains(id: number) {
return this.#masked.has(id);
}
#updateMasked() {
this.#masked = new Set([...this.#ids].filter((i) => this.#indexOf(i) >= 0));
}
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);
}
}
|