New filterbar

This commit is contained in:
Tom Hubrecht 2022-03-06 15:07:50 +01:00
parent d31a4bbcc4
commit 22c1b4a784
3 changed files with 81 additions and 28 deletions

View file

@ -35,6 +35,7 @@
bootstrap5Plugin bootstrap5Plugin
], ],
locale: frLocale, locale: frLocale,
allDayContent: '',
headerToolbar: { headerToolbar: {
left: 'prev,next today', left: 'prev,next today',
center: 'title', center: 'title',
@ -48,6 +49,7 @@
openModal = true; openModal = true;
event.set(info.event); event.set(info.event);
}, },
titleFormat: { year: '2-digit', month: 'numeric', day: 'numeric' },
events: [], events: [],
themeSystem: 'bootstrap5' themeSystem: 'bootstrap5'
}); });
@ -82,13 +84,16 @@
<div class="app-container"> <div class="app-container">
<h1 class="title">Calendrier de la vie étudiante à l'ENS</h1> <h1 class="title">Calendrier de la vie étudiante à l'ENS</h1>
<div style="height: 100%; display: flex;">
<FilterBar {calendarTree} bind:selected={selectedCalendars} /> <FilterBar {calendarTree} bind:selected={selectedCalendars} />
<div style="flex: 1;">
<div class="h-100 d-flex">
<div class="flex-grow-1">
<FullCalendar bind:this={calendar} options={$options} /> <FullCalendar bind:this={calendar} options={$options} />
</div> </div>
<EventModal event={$event} open={openModal} {toggle} />
</div> </div>
<EventModal event={$event} open={openModal} {toggle} />
</div> </div>
<style> <style>

View file

@ -1,14 +1,29 @@
<script> <script>
import { Button, Offcanvas, Icon } from 'sveltestrap';
import FilterItem from './FilterItem.svelte'; import FilterItem from './FilterItem.svelte';
export let calendarTree = []; export let calendarTree = [];
export let selected = null; export let selected = null;
let subSelections = Array.from({length: Object.keys(calendarTree).length}, e => []); let subSelections = Array.from({ length: Object.keys(calendarTree).length }, _ => []);
$: selected = subSelections.flat(); $: selected = subSelections.flat();
let open = true;
const toggle = () => (open = !open);
</script> </script>
<ul> <Button on:click={toggle} class="my-2">
{#each Object.entries(calendarTree) as [toplevel, subtrees], i} <span class="me-2">Sélection des calendriers</span>
<FilterItem item={toplevel} children={subtrees} bind:selected={subSelections[i]} /> <Icon name="chevron-double-right" />
{/each} </Button>
</ul>
<Offcanvas
isOpen={open}
{toggle}
scroll
placement="start"
header="Sélection des calendriers"
>
{#each Object.entries(calendarTree) as [toplevel, subtrees], i}
<FilterItem item={toplevel} children={subtrees} bind:selected={subSelections[i]} />
{/each}
</Offcanvas>

View file

@ -4,6 +4,7 @@
import TriStateCheckbox from './TriStateCheckbox.svelte'; import TriStateCheckbox from './TriStateCheckbox.svelte';
import { createTriState, createTriStates, UNCHECKED, CHECKED, WEIRD } from './stores'; import { createTriState, createTriStates, UNCHECKED, CHECKED, WEIRD } from './stores';
import { Icon } from 'sveltestrap';
export let item = null; export let item = null;
@ -11,12 +12,12 @@
export let selected = []; export let selected = [];
export let filtering = createTriState(CHECKED); export let filtering = createTriState(CHECKED);
let subfiltering = createTriStates(CHECKED, Object.entries(children).length); let subfiltering = createTriStates(CHECKED, Object.entries(children).length);
let subselected = Array.from({length: Object.entries(children).length}, e => []); let subselected = Array.from({ length: Object.entries(children).length }, e => []);
function isVal(val) { function isVal(val) {
return function (other) { return function (other) {
return other === val; return other === val;
} };
} }
function areAllChecked(values) { function areAllChecked(values) {
@ -30,7 +31,7 @@
function handleChildChange(i) { function handleChildChange(i) {
return function handler(evt) { return function handler(evt) {
subfiltering.setAt(i, evt.detail.value); subfiltering.setAt(i, evt.detail.value);
} };
} }
subfiltering.subscribe(subvalues => { subfiltering.subscribe(subvalues => {
@ -38,7 +39,11 @@
filtering.setChecked(); filtering.setChecked();
} else if (areAllUnchecked(subvalues) && $filtering !== UNCHECKED) { } else if (areAllUnchecked(subvalues) && $filtering !== UNCHECKED) {
filtering.setUnchecked(); filtering.setUnchecked();
} else if (!areAllChecked(subvalues) && !areAllUnchecked(subvalues) && $filtering !== WEIRD) { } else if (
!areAllChecked(subvalues) &&
!areAllUnchecked(subvalues) &&
$filtering !== WEIRD
) {
filtering.setWeird(); filtering.setWeird();
} }
}); });
@ -62,21 +67,49 @@
dispatch('change', { value: evt.detail.value }); dispatch('change', { value: evt.detail.value });
} }
$: selected = $filtering === CHECKED ? [item, ...subselected.flat()] : subselected.flat(); $: 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> </script>
{#if item != null} {#if item != null}
<li> <li>
<TriStateCheckbox state={$filtering} on:change={handleChange} value={item} initialValue={true} /> <TriStateCheckbox
<span>{item}</span> state={$filtering}
<ul> on:change={handleChange}
{#each Object.entries(children) as [toplevel, subchildren], i} value={item}
{#if subchildren} initialValue={true}
<svelte:self item={toplevel} children={subchildren} on:change={handleChildChange(i)} filtering={subfiltering.storeAt(i)} bind:selected={subselected[i]} /> />
{:else} <span><Icon name={icon()} />{item}</span>
<svelte:self item={toplevel} on:change={handleChildChange(i)} filtering={subfiltering.storeAt(i)} bind:selected={subselected[i]} /> <ul>
{/if} {#each Object.entries(children) as [toplevel, subchildren], i}
{/each} {#if subchildren}
</ul> <svelte:self
</li> 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} {/if}