feat: at calm, everything is better.
This commit is contained in:
parent
ee7fb9b70a
commit
da2f53d86a
4 changed files with 95 additions and 57 deletions
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue