Skip to content

Commit

Permalink
feat(lxl-web, supersearch): Display qualifier labels (LWS-274) (#1186)
Browse files Browse the repository at this point in the history
* Add getLabelsFromMapping util function
* Refactor lxlQualifier plugin
* Svelte 5-migration of lxlweb/Search component
* Update codemirror state on lxlweb navigation
* Adjust response from server.ts
  • Loading branch information
jesperengstrom authored Dec 18, 2024
1 parent 3aef8ac commit acb4405
Show file tree
Hide file tree
Showing 18 changed files with 588 additions and 208 deletions.
75 changes: 50 additions & 25 deletions lxl-web/src/lib/components/Search.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,29 @@
import { SuperSearch, lxlQualifierPlugin } from 'supersearch';
import addDefaultSearchParams from '$lib/utils/addDefaultSearchParams';
import getSortedSearchParams from '$lib/utils/getSortedSearchParams';
import getLabelFromMappings from '$lib/utils/getLabelsFromMapping.svelte';
import type { DisplayMapping } from '$lib/types/search';
import BiSearch from '~icons/bi/search';
import { lxlQuery } from 'codemirror-lang-lxlquery';
import '$lib/styles/lxlquery.css';
export let placeholder: string;
export let autofocus: boolean = false;
interface Props {
placeholder: string;
autofocus?: boolean;
}
let { placeholder, autofocus = false }: Props = $props();
const useSuperSearch = env?.PUBLIC_USE_SUPERSEARCH === 'true';
const showAdvanced = $page.url.searchParams.get('_x') === 'advanced' || useSuperSearch;
$: showAdvanced = $page.url.searchParams.get('_x') === 'advanced';
let q = $page.params.fnurgel
? '' //don't reflect related search on resource pages
: showAdvanced
? $page.url.searchParams.get('_q')?.trim() || ''
: $page.url.searchParams.get('_i')?.trim() || '';
let q = $state(
$page.params.fnurgel
? '' //don't reflect related search on resource pages
: showAdvanced
? $page.url.searchParams.get('_q')?.trim() || ''
: $page.url.searchParams.get('_i')?.trim() || ''
);
let params = getSortedSearchParams(addDefaultSearchParams($page.url.searchParams));
// Always reset these params on new search
Expand All @@ -27,10 +37,12 @@
params.delete('_p');
const searchParams = Array.from(params);
let suggestMapping: DisplayMapping[] | undefined = $state();
afterNavigate(({ to }) => {
/** Update input value after navigation on /find route */
if (to?.url) {
let param = showAdvanced ? '_q' : '_i';
let param = $page.url.searchParams.get('_x') === 'advanced' || useSuperSearch ? '_q' : '_i';
q = $page.params.fnurgel ? '' : new URL(to.url).searchParams.get(param)?.trim() || '';
}
});
Expand All @@ -54,10 +66,23 @@
}
return undefined;
}
function handleTransform(data) {
suggestMapping = data?.mapping;
return data;
}
let derivedLxlQualifierPlugin = $derived.by(() => {
function getLabels(key: string, value?: string) {
let pageMapping = $page.data.searchResult?.mapping;
return getLabelFromMappings(key, value, pageMapping, suggestMapping);
}
return lxlQualifierPlugin(getLabels);
});
</script>

<form class="relative w-full" action="find" on:submit={handleSubmit}>
{#if env?.PUBLIC_USE_SUPERSEARCH === 'true'}
<form class="relative w-full" action="find" onsubmit={handleSubmit}>
{#if useSuperSearch}
<SuperSearch
name="_q"
bind:value={q}
Expand All @@ -71,8 +96,9 @@
cursor: cursor.toString()
});
}}
transformFn={handleTransform}
paginationQueryFn={handlePaginationQuery}
extensions={[lxlQualifierPlugin]}
extensions={[derivedLxlQualifierPlugin]}
>
{#snippet resultItem(item)}
<button type="button">
Expand All @@ -81,7 +107,7 @@
{/snippet}
</SuperSearch>
{:else}
<!-- svelte-ignore a11y-autofocus -->
<!-- svelte-ignore a11y_autofocus -->
<input
id="main-search"
class="h-12 w-full rounded-full pr-12 text-secondary shadow-accent-dark/32 focus:shadow-search-focus focus:outline
Expand All @@ -96,24 +122,23 @@
{autofocus}
data-testid="main-search"
/>
{#each searchParams as [name, value]}
{#if name !== '_q'}
<input type="hidden" {name} {value} />
{/if}
{/each}

<input type="hidden" name="_i" value={q} />
{#if $page.url.searchParams.get('_x') === 'advanced'}
<!-- keep 'edit' state on new search -->
<input type="hidden" name="_x" value="advanced" />
{/if}

<button
type="submit"
class="button-primary absolute right-1 top-1 rounded-full px-3 sm:right-2 sm:top-2 sm:px-4"
>
<BiSearch fill="currentColor" aria-hidden="true" />
<span class="sr-only sm:not-sr-only">{$page.data.t('search.search')}</span>
</button>
<input type="hidden" name="_i" value={q} />
{#if $page.url.searchParams.get('_x') === 'advanced'}
<!-- keep 'edit' state on new search -->
<input type="hidden" name="_x" value="advanced" />
{/if}
{/if}
<!-- params used by all search modes -->
{#each searchParams as [name, value]}
{#if name !== '_q'}
<input type="hidden" {name} {value} />
{/if}
{/each}
</form>
44 changes: 37 additions & 7 deletions lxl-web/src/lib/styles/lxlquery.css
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
.lxl-qualifier,
.lxl-qualifier-remove {
background: rgba(14, 113, 128, 0.15);
padding: 2px 5px;
.lxl-qualifier {
display: inline-flex;
color: rgb(0, 128, 0);
background: rgba(14, 113, 128, 0.1);
padding-top: 3px;
padding-bottom: 3px;
}

.lxl-qualifier-key {
color: green;
padding-left: 5px;
border-top-left-radius: 5px;
border-bottom-left-radius: 5px;
}

.lxl-qualifier-value,
.lxl-qualifier-remove {
padding-right: 5px;
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
}

.lxl-qualifier-remove,
.lxl-qualifier-value.atomic {
padding-left: 5px;
}

.lxl-qualifier-value:has(~ .lxl-qualifier-remove) {
border-radius: 0;
}

.invalid > .lxl-qualifier-key {
text-decoration: underline 2px solid;
text-decoration-color: rgba(255, 0, 0, 0.326);
}

.atomic {
background: rgba(14, 113, 128, 0.2);
user-select: none;
}

.lxl-boolean-query {
color: purple;
color: rgb(128, 0, 128);
}

.lxl-wildcard {
color: purple;
color: rgb(128, 0, 128);
}
2 changes: 2 additions & 0 deletions lxl-web/src/lib/types/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,12 @@ interface SpellingSuggestion {
export interface DisplayMapping {
'@id'?: string;
display?: DisplayDecorated;
displayStr?: string;
up?: Link;
children?: DisplayMapping[];
label?: string;
operator: keyof typeof SearchOperators;
property?: string;
}

export interface PartialCollectionView {
Expand Down
62 changes: 62 additions & 0 deletions lxl-web/src/lib/utils/getLabelsFromMapping.svelte.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { DisplayMapping } from '$lib/types/search';

let prevSuggestMapping: DisplayMapping[] | undefined;

function getLabelFromMappings(
key: string,
value?: string,
pageMapping?: DisplayMapping[],
suggestMapping?: DisplayMapping[]
) {
const pageLabels = iterateMapping(key, value, pageMapping);
const suggestLabels = iterateMapping(key, value, suggestMapping || prevSuggestMapping);

const keyLabel = suggestLabels.keyLabel || pageLabels.keyLabel;
const valueLabel = suggestLabels.valueLabel || pageLabels.valueLabel;
// only page data have 'up' links we can use
const removeLink = pageLabels.keyLabel ? pageLabels.removeLink : undefined;

if (suggestMapping) {
// TODO remove when invalid qualifier no longer result in empty error response
// until when we need to save latest 'successful' suggest mapping
prevSuggestMapping = suggestMapping;
}

return { keyLabel, valueLabel, removeLink };
}

function iterateMapping(
key: string,
value: string | undefined,
mapping: DisplayMapping[] | undefined | null
) {
let keyLabel: string | undefined;
let valueLabel: string | undefined;
let removeLink: string | undefined;

if (mapping && Array.isArray(mapping)) {
_iterate(mapping);

function _iterate(m: DisplayMapping[]) {
m.forEach((el) => {
if (el.children) {
_iterate(el.children);
} else if (el.property === key) {
keyLabel = el.label;
const isLinked = !!el.display?.['@id'];
if (isLinked) {
if (el.displayStr) {
// only use atomic ranges for linked values
valueLabel = el.displayStr;
}
// only show remove btn for pills that can't be edited
removeLink = el.up?.['@id'];
}
}
});
}
}
return { keyLabel, valueLabel, removeLink };
}

export default getLabelFromMappings;
Loading

0 comments on commit acb4405

Please sign in to comment.