feat: at calm, everything is better.

This commit is contained in:
Raito Bezarius 2022-03-05 21:04:19 +01:00
parent ee7fb9b70a
commit da2f53d86a
4 changed files with 95 additions and 57 deletions

View file

@ -44,7 +44,7 @@ export default {
compilerOptions: {
// enable run-time checks when not in production
dev: !production
}
},
}),
// we'll extract any component CSS out into
// a separate file - better for performance

View file

@ -1,74 +1,80 @@
<script>
import { writable } from 'svelte/store';
import { tick } from 'svelte';
import { tick, createEventDispatcher } from 'svelte';
import TriStateCheckbox from './TriStateCheckbox.svelte';
import { createTriState, UNCHECKED, CHECKED, WEIRD } from './stores';
import { createTriState, createTriStates, UNCHECKED, CHECKED, WEIRD } from './stores';
export let item = null;
export let children = [];
export let selected = [];
export let filtering = createTriState(CHECKED);
let subfiltering = Array(Object.keys(children).length).fill(createTriState(CHECKED));
let subfiltering = createTriStates(CHECKED, Object.entries(children).length);
function isVal(val) {
return function (other) {
return other === val;
}
}
subfiltering.forEach((subfilter, j) => {
subfilter.subscribe(subvalue => {
console.log('subfiltering', Object.entries(children)[j][0], 'newValue=', subvalue, 'prevValue=', subfiltering[j], '$filtering=', $filtering);
const others = subfiltering.filter((_, i) => i !== j);
const areAllOthers = others.every(isVal(CHECKED));
const isThereAny = !others.some(isVal(UNCHECKED));
function areAllChecked(values) {
return values.every(isVal(CHECKED));
}
tick().then(() => {
if (subvalue === CHECKED && areAllOthers && $filtering !== CHECKED) {
console.log('check $filtering');
filtering.setChecked();
} else if (subvalue === UNCHECKED && isThereAny && $filtering !== UNCHECKED) {
console.log('uncheck $filtering');
filtering.setUnchecked();
} else if (!areAllOthers && !isThereAny && $filtering !== WEIRD) {
console.log(Object.entries(children)[j][0], 'partial checking');
filtering.setWeird();
}
});
});
});
function areAllUnchecked(values) {
return values.every(isVal(UNCHECKED));
}
function handleChildChange(i) {
return function handler(evt) {
subfiltering.setAt(i, evt.detail.value);
}
}
filtering.subscribe(value => {
console.log('filtering', item, 'newValue=', value, 'prevValue=', $filtering);
if (value !== $filtering) {
switch (value) {
case CHECKED:
subfiltering.forEach(subfilter => subfilter.setChecked());
break;
case UNCHECKED:
subfiltering.forEach(subfilter => subfilter.setUnchecked());
break;
}
subfiltering.subscribe(subvalues => {
console.log('new subvalues', 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 => {
console.log(item, 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 });
}
</script>
{#if item != null}
<li>
<TriStateCheckbox bind:state={filtering} value={item} initialValue={true} />
<TriStateCheckbox state={$filtering} on:change={handleChange} value={item} initialValue={true} />
<span>{item}</span>
<ul>
{#each Object.entries(children) as [toplevel, subchildren], i}
{#if subchildren}
<svelte:self item={toplevel} children={subchildren} bind:filtering={subfiltering[i]} />
<svelte:self item={toplevel} children={subchildren} on:change={handleChildChange(i)} filtering={subfiltering.storeAt(i)} />
{:else}
<svelte:self item={toplevel} bind:filtering={subfiltering[i]} />
<svelte:self item={toplevel} on:change={handleChildChange(i)} filtering={subfiltering.storeAt(i)} />
{/if}
{/each}
</ul>

View file

@ -3,31 +3,35 @@
export let state;
export let value;
import { createTriState, UNCHECKED, CHECKED, WEIRD } from './stores';
import { UNCHECKED, CHECKED, WEIRD } from './stores';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
let node;
let checkedValue = false;
state.subscribe(val => {
if (!node) return;
$: {
if (node) {
if (state === CHECKED) {
checkedValue = true;
} else if (state === UNCHECKED) {
checkedValue = false;
}
if (val === CHECKED) {
checkedValue = true;
} else if (val === UNCHECKED) {
checkedValue = false;
}
node.indeterminate = state === WEIRD;
node.indeterminate = val === WEIRD;
});
function handleClick(evt) {
if (evt.target.checked) {
state.setChecked();
} else if (!evt.target.checked) {
state.setUnchecked();
}
}
let checkedValue = false;
function handleClick(evt) {
if (evt.target.checked) {
state = CHECKED;
} else if (!evt.target.checked) {
state = UNCHECKED;
}
dispatch('change', { value: state });
}
</script>
<input type="checkbox" checked={checkedValue} on:click={handleClick} bind:this={node} value={value} />

View file

@ -16,3 +16,31 @@ export function createTriState(initialValue) {
}
}
export function createTriStates(initialValue, length) {
const { subscribe, update, set } = writable(Array.from({length}, e => initialValue));
const setAt = (index, newValue) => {
update(array => [...array.slice(0, index), newValue, ...array.slice(index + 1)]);
};
return {
subscribe,
set,
setAt,
length,
storeAt: index => {
const set = value => setAt(index, value);
return {
subscribe: fun => subscribe(subvalues => fun(subvalues[index])),
set,
setUnchecked: () => set(UNCHECKED),
setWeird: () => set(WEIRD),
setChecked: () => set(CHECKED)
};
},
updateAll: fun => {
update(array => array.map(fun));
}
}
}