metis/src/FilterItem.svelte
2022-03-06 15:07:50 +01:00

115 lines
2.9 KiB
Svelte

<script>
import { writable } from 'svelte/store';
import { tick, createEventDispatcher } from 'svelte';
import TriStateCheckbox from './TriStateCheckbox.svelte';
import { createTriState, createTriStates, UNCHECKED, CHECKED, WEIRD } from './stores';
import { Icon } from 'sveltestrap';
export let item = null;
export let children = [];
export let selected = [];
export let filtering = createTriState(CHECKED);
let subfiltering = createTriStates(CHECKED, Object.entries(children).length);
let subselected = Array.from({ length: Object.entries(children).length }, e => []);
function isVal(val) {
return function (other) {
return other === val;
};
}
function areAllChecked(values) {
return values.every(isVal(CHECKED));
}
function areAllUnchecked(values) {
return values.every(isVal(UNCHECKED));
}
function handleChildChange(i) {
return function handler(evt) {
subfiltering.setAt(i, evt.detail.value);
};
}
subfiltering.subscribe(subvalues => {
if (areAllChecked(subvalues) && $filtering !== CHECKED) {
filtering.setChecked();
} else if (areAllUnchecked(subvalues) && $filtering !== UNCHECKED) {
filtering.setUnchecked();
} else if (
!areAllChecked(subvalues) &&
!areAllUnchecked(subvalues) &&
$filtering !== WEIRD
) {
filtering.setWeird();
}
});
if (subfiltering.length > 0) {
filtering.subscribe(value => {
switch (value) {
case CHECKED:
subfiltering.updateAll(_ => CHECKED);
break;
case UNCHECKED:
subfiltering.updateAll(_ => UNCHECKED);
break;
}
});
}
const dispatch = createEventDispatcher();
function handleChange(evt) {
filtering.set(evt.detail.value);
dispatch('change', { value: evt.detail.value });
}
$: icon = () => {
switch ($filtering) {
case UNCHECKED:
return 'circle';
case WEIRD:
return 'circle-half';
case CHECKED:
return 'check-circle-fill';
}
};
$: selected =
$filtering === CHECKED ? [item, ...subselected.flat()] : subselected.flat();
</script>
{#if item != null}
<li>
<TriStateCheckbox
state={$filtering}
on:change={handleChange}
value={item}
initialValue={true}
/>
<span><Icon name={icon()} />{item}</span>
<ul>
{#each Object.entries(children) as [toplevel, subchildren], i}
{#if subchildren}
<svelte:self
item={toplevel}
children={subchildren}
on:change={handleChildChange(i)}
filtering={subfiltering.storeAt(i)}
bind:selected={subselected[i]}
/>
{:else}
<svelte:self
item={toplevel}
on:change={handleChildChange(i)}
filtering={subfiltering.storeAt(i)}
bind:selected={subselected[i]}
/>
{/if}
{/each}
</ul>
</li>
{/if}