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: {
|
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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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} />
|
||||||
|
|
|
@ -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