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: { compilerOptions: {
// enable run-time checks when not in production // enable run-time checks when not in production
dev: !production dev: !production
} },
}), }),
// we'll extract any component CSS out into // we'll extract any component CSS out into
// a separate file - better for performance // a separate file - better for performance

View file

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

View file

@ -3,31 +3,35 @@
export let state; export let state;
export let value; 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 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) { node.indeterminate = state === WEIRD;
checkedValue = true;
} else if (val === UNCHECKED) {
checkedValue = false;
}
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> </script>
<input type="checkbox" checked={checkedValue} on:click={handleClick} bind:this={node} value={value} /> <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));
}
}
}