Skip to content

Commit

Permalink
Set cookie to save sort selection
Browse files Browse the repository at this point in the history
  • Loading branch information
jesperengstrom committed Sep 13, 2024
1 parent 5a9fcc2 commit ea1f31c
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 14 deletions.
17 changes: 17 additions & 0 deletions lxl-web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions lxl-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@sveltejs/vite-plugin-svelte": "^3.1.1",
"@types/eslint": "^9.6.0",
"@types/jmespath": "^0.15.2",
"@types/js-cookie": "^3.0.6",
"autoprefixer": "^10.4.20",
"cssnano": "^7.0.5",
"eslint": "^9.9.0",
Expand All @@ -42,6 +43,7 @@
"globals": "^15.9.0",
"husky": "^9.1.4",
"jmespath": "^0.16.0",
"js-cookie": "^3.0.5",
"lint-staged": "^15.2.9",
"lxljs": "file:../lxljs",
"magic-string": "^0.30.11",
Expand Down
2 changes: 2 additions & 0 deletions lxl-web/src/app.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
import type { UserSettings } from '$lib/types/userSettings';
import 'unplugin-icons/types/svelte';

declare global {
Expand All @@ -11,6 +12,7 @@ declare global {
interface PageData {
locale: import('$lib/i18n/locales').LocaleCode;
t: Awaited<ReturnType<typeof import('$lib/i18n').getTranslator>>;
userSettings: UserSettings;
}
interface PageState {
expandedInstances?: string[];
Expand Down
92 changes: 82 additions & 10 deletions lxl-web/src/lib/components/find/FacetGroup.svelte
Original file line number Diff line number Diff line change
@@ -1,36 +1,78 @@
<script lang="ts">
import type { LocaleCode } from '$lib/i18n/locales';
import { page } from '$app/stores';
import { type FacetGroup } from '$lib/types/search';
import type { FacetGroup, Facet, MultiSelectFacet } from '$lib/types/search';
import BiChevronRight from '~icons/bi/chevron-right';
import BiSortDown from '~icons/bi/sort-down';
import CheckSquareFill from '~icons/bi/check-square-fill';
import Square from '~icons/bi/square';
import FacetRange from './FacetRange.svelte';
import DecoratedData from '../DecoratedData.svelte';
import { ShowLabelsOptions } from '$lib/types/decoratedData';
import { DEFAULT_FACET_SORT, DEFAULT_FACETS_SHOWN } from '$lib/constants/facets';
import { saveUserSetting } from '$lib/utils/userSettings';
export let group: FacetGroup;
export let locale: LocaleCode;
export let searchPhrase = '';
const defaultFacetsShown = 5;
let facetsShown = defaultFacetsShown;
let facetsShown = DEFAULT_FACETS_SHOWN;
const userSort = $page.data.userSettings?.facetSort?.[group.dimension];
let currentSort = userSort || DEFAULT_FACET_SORT;
const sortOptions = [
{ value: 'hits.desc', label: $page.data.t('sort.hitsDesc') },
{ value: 'hits.asc', label: $page.data.t('sort.hitsAsc') },
{ value: 'alpha.asc', label: getAlphaLabel('Asc') },
{ value: 'alpha.desc', label: getAlphaLabel('Desc') }
];
function getAlphaLabel(dir: 'Asc' | 'Desc' = 'Desc'): string {
let key = group.dimension === 'yearPublished' ? `sort.year${dir}` : `sort.alpha${dir}`;
return $page.data.t(key);
}
$: sortFn = (a: Facet | MultiSelectFacet, b: Facet | MultiSelectFacet): number => {
let l = $page.data.locale;
switch (currentSort) {
case 'hits.asc':
return b.totalItems > a.totalItems ? -1 : 1;
case 'alpha.desc':
return b.str.localeCompare(a.str, l);
case 'alpha.asc':
return a.str.localeCompare(b.str, l);
default:
// hits.desc
return b.totalItems < a.totalItems ? -1 : 1;
}
};
function saveUserSort(e: Event): void {
const target = e.target as HTMLSelectElement;
saveUserSetting('facetSort', { [group.dimension]: target.value });
}
$: numfacets = group.facets.length;
$: hasHits = filteredFacets.length > 0;
$: expanded = searchPhrase && hasHits;
$: filteredFacets = group.facets.filter((facet) =>
$: sortedFacets = group.facets.sort(sortFn);
$: filteredFacets = sortedFacets.filter((facet) =>
facet.str
.toLowerCase()
.split(/\s|--/)
.find((s) => s.startsWith(searchPhrase.toLowerCase()))
);
$: shownFacets = filteredFacets.filter((facet, index) => index < facetsShown);
$: canShowMoreFacets = filteredFacets.length > facetsShown;
$: canShowLessFacets = !canShowMoreFacets && filteredFacets.length > defaultFacetsShown;
$: canShowLessFacets = !canShowMoreFacets && filteredFacets.length > DEFAULT_FACETS_SHOWN;
</script>

<li class="border-b border-primary/16 first:border-t" class:hidden={searchPhrase && !hasHits}>
<li
class="border-b border-primary/16 first:border-t"
class:hidden={searchPhrase && !hasHits}
data-dimension={group.dimension}
>
<details open={!!expanded}>
<summary
class="flex min-h-11 w-full cursor-pointer items-center gap-2 font-bold"
Expand All @@ -39,7 +81,21 @@
<span class="arrow transition-transform">
<BiChevronRight class="text-icon" />
</span>
<span>{group.label}</span>
<span class="flex-1 whitespace-nowrap">{group.label}</span>
<!-- sorting -->
<div class="facet-sort relative hidden">
<select
bind:value={currentSort}
on:change={saveUserSort}
class="rounded-sm border border-primary/8 px-2 py-1 pl-5 text-2-regular"
aria-label={$page.data.t('sort.sort') + ' ' + $page.data.t('search.filters')}
>
{#each sortOptions as option}
<option value={option.value}>{option.label}</option>
{/each}
</select>
<BiSortDown class="absolute top-0 m-1.5 text-icon-strong" />
</div>
</summary>
<div class="mb-4 md:text-sm lg:text-base">
{#if group.search && !(searchPhrase && hasHits)}
Expand Down Expand Up @@ -92,7 +148,7 @@
<button
class="ml-6 mt-4 underline"
on:click={() =>
canShowMoreFacets ? (facetsShown = numfacets) : (facetsShown = defaultFacetsShown)}
canShowMoreFacets ? (facetsShown = numfacets) : (facetsShown = DEFAULT_FACETS_SHOWN)}
>
{canShowMoreFacets ? $page.data.t('search.showMore') : $page.data.t('search.showFewer')}
</button>
Expand All @@ -105,8 +161,18 @@
</li>

<style lang="postcss">
details[open] .arrow {
@apply rotate-90;
details[open] {
& .arrow {
@apply rotate-90;
}
& .facet-sort {
@apply block;
}
}
/* hide sorting for bool filters */
li[data-dimension='boolFilters'] details[open] .facet-sort {
@apply hidden;
}
.facet-link:hover,
Expand All @@ -115,4 +181,10 @@
@apply bg-pill/8;
}
}
select {
@apply text-right;
/* Safari text-align fix */
text-align-last: right;
}
</style>
2 changes: 2 additions & 0 deletions lxl-web/src/lib/constants/facets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const DEFAULT_FACETS_SHOWN = 5;
export const DEFAULT_FACET_SORT = 'hits.desc';
6 changes: 5 additions & 1 deletion lxl-web/src/lib/i18n/locales/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ export default {
alphaDesc: 'Z-A',
publicationAsc: 'Publication year (oldest first)',
publicationDesc: 'Publication year (newest first)',
linksDesc: 'Most linked'
linksDesc: 'Most linked',
hitsAsc: 'Hits asc.',
hitsDesc: 'Hits desc.',
yearAsc: 'Oldest first',
yearDesc: 'Newest first'
},
errors: {
somethingWentWrong: 'Something went wrong',
Expand Down
6 changes: 5 additions & 1 deletion lxl-web/src/lib/i18n/locales/sv.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ export default {
alphaDesc: 'Ö-A',
publicationAsc: 'Utgivningsår (äldst först)',
publicationDesc: 'Utgivningsår (nyast först)',
linksDesc: 'Mest länkad'
linksDesc: 'Mest länkad',
hitsAsc: 'Minst träffar',
hitsDesc: 'Flest träffar',
yearAsc: 'Äldst först',
yearDesc: 'Nyast först'
},
errors: {
somethingWentWrong: 'Något gick fel',
Expand Down
7 changes: 7 additions & 0 deletions lxl-web/src/lib/types/userSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export type UserSettings = SettingsObj | undefined;

interface SettingsObj {
facetSort: {
[dimension: string]: string;
};
}
30 changes: 30 additions & 0 deletions lxl-web/src/lib/utils/userSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Cookies from 'js-cookie';
import { browser } from '$app/environment';
import type { UserSettings } from '$lib/types/userSettings';

export function saveUserSetting(namespace: 'facetSort', value: { [dimension: string]: string }) {
if (browser) {
const userSettings = Cookies.get('userSettings');
let newSettings;

if (userSettings) {
try {
const parsedSettings = JSON.parse(userSettings) as UserSettings;
if (parsedSettings) {
newSettings = {
...parsedSettings,
[namespace]: {
...(namespace in parsedSettings && parsedSettings[namespace]),
...value
}
};
}
} catch (e) {
console.warn(e);
}
} else {
newSettings = { [namespace]: value };
}
Cookies.set('userSettings', JSON.stringify(newSettings));
}
}
16 changes: 16 additions & 0 deletions lxl-web/src/routes/+layout.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { UserSettings } from '$lib/types/userSettings.js';

export async function load({ cookies }) {
let userSettings: UserSettings;
const settingsCookie = cookies.get('userSettings');
if (settingsCookie) {
try {
userSettings = JSON.parse(settingsCookie);
} catch (e) {
console.warn('Failed to parse user settings', e);
}
}
return {
userSettings
};
}
5 changes: 3 additions & 2 deletions lxl-web/src/routes/+layout.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { getTranslator } from '$lib/i18n/index.js';
import { getSupportedLocale, defaultLocale } from '$lib/i18n/locales';

export async function load({ params }) {
export async function load({ params, data }) {
const locale = getSupportedLocale(params?.lang); // will use default locale if no lang param
const t = await getTranslator(locale);
const base = locale === defaultLocale ? '/' : `/${locale}/`;
return { locale, t, base };
const userSettings = data.userSettings;
return { locale, t, base, userSettings };
}

0 comments on commit ea1f31c

Please sign in to comment.