Skip to content

Commit 19cc689

Browse files
authored
Add semantic domain filter (#2041)
* Make field filter-select match other filter-selects * Add semantic domain filter * Ensure default field selection is pushed to parent * Disabled ws-select if no type is provided * Make filter selects clearable with x-button * Standardize height of selects * Standardize styles and wording
1 parent e8a8232 commit 19cc689

File tree

16 files changed

+459
-223
lines changed

16 files changed

+459
-223
lines changed

frontend/viewer/src/lib/components/field-editors/multi-select.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@
185185

186186
{#snippet trigger({ props }: { props: Record<string, unknown> })}
187187
<Button disabled={readonly} bind:ref={triggerRef} variant="outline" {...props} role="combobox" aria-expanded={open}
188-
class="w-full h-auto px-2 justify-between disabled:opacity-100 disabled:border-transparent">
188+
class="w-full h-auto min-h-10 px-2 justify-between disabled:opacity-100 disabled:border-transparent">
189189
{@render displayBadges()}
190190
{#if !readonly}
191191
<Icon icon="i-mdi-chevron-down" class="mr-2 size-5 shrink-0 opacity-50" />

frontend/viewer/src/lib/components/field-editors/select.svelte

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,29 @@
2020
value?: Value;
2121
options: ReadonlyArray<Value>;
2222
readonly?: boolean;
23+
clearable?: boolean;
2324
idSelector: ConditionalKeys<Value, Primitive> | ((value: Value) => Primitive);
2425
labelSelector: ConditionalKeys<Value, string> | ((value: Value) => string);
2526
placeholder?: string;
2627
filterPlaceholder?: string;
2728
emptyResultsPlaceholder?: string;
2829
drawerTitle?: string;
29-
onchange?: (value: Value) => void;
30+
onchange?: (value: Value | undefined) => void;
3031
class?: string;
3132
} = $props();
3233
3334
const {
3435
options,
3536
readonly = false,
37+
clearable = false,
3638
idSelector,
3739
labelSelector,
3840
placeholder,
3941
filterPlaceholder,
4042
emptyResultsPlaceholder,
4143
drawerTitle,
4244
onchange,
43-
class: className
45+
class: className,
4446
} = $derived(constProps);
4547
4648
function getId(value: Value): Primitive {
@@ -66,7 +68,7 @@
6668
open = false;
6769
}
6870
69-
function selectValue(newValue: Value) {
71+
function selectValue(newValue: Value | undefined) {
7072
value = newValue;
7173
onchange?.(newValue);
7274
open = false;
@@ -88,8 +90,9 @@
8890
</script>
8991

9092
{#snippet trigger({ props }: { props: Record<string, unknown> })}
91-
<Button disabled={readonly} bind:ref={triggerRef} variant="outline" {...props} role="combobox" aria-expanded={open}
92-
class={cn('w-full h-auto px-2 justify-between disabled:opacity-100 disabled:border-transparent', className)}>
93+
<div class="relative">
94+
<Button disabled={readonly} bind:ref={triggerRef} variant="outline" {...props} role="combobox" aria-expanded={open}
95+
class={cn('w-full h-auto min-h-10 px-2 justify-between disabled:opacity-100 disabled:border-transparent', className)}>
9396
{#if value}
9497
<span>
9598
{getLabel(value)}
@@ -100,11 +103,17 @@
100103
<!-- ensures that baseline alignment works for consumers of this component -->
101104
&nbsp;
102105
</span>
103-
{/if}
106+
{/if}
107+
</Button>
104108
{#if !readonly}
105-
<Icon icon="i-mdi-chevron-down" class="mr-2 size-5 shrink-0 opacity-50" />
109+
<div class="absolute right-0 z-10 top-1/2 -translate-y-1/2 flex items-center gap-0.5 pointer-events-none">
110+
{#if clearable}
111+
<XButton onclick={() => selectValue(undefined)} aria-label={$t`clear`} class={cn('pointer-events-auto', value || 'invisible')} />
112+
{/if}
113+
<Icon icon="i-mdi-chevron-down" class="mr-2 size-5 shrink-0 opacity-50" />
114+
</div>
106115
{/if}
107-
</Button>
116+
</div>
108117
{/snippet}
109118

110119
{#snippet command()}
Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,46 @@
11
<script lang="ts">
22
import {cn} from '$lib/utils.js';
3-
import {Select as SelectPrimitive, type WithoutChild} from 'bits-ui';
3+
import {mergeProps, Select as SelectPrimitive, type WithoutChild} from 'bits-ui';
44
import {Icon} from '../icon';
55
import type {IconClass} from '$lib/icon-class';
6+
import XButton from '../button/x-button.svelte';
7+
import type {ComponentProps} from 'svelte';
68
79
let {
810
ref = $bindable(null),
911
class: className,
1012
downIcon = 'i-mdi-chevron-down',
1113
children,
14+
onClear,
15+
style: styleProps,
1216
...restProps
1317
}: WithoutChild<SelectPrimitive.TriggerProps> & {
1418
downIcon?: IconClass | null;
19+
onClear?: ComponentProps<typeof XButton>['onclick'];
1520
} = $props();
21+
22+
// turn style into a normal html format
23+
const style = $derived(mergeProps({style: styleProps}).style);
1624
</script>
1725

18-
<SelectPrimitive.Trigger
19-
bind:ref
20-
class={cn(
21-
'border-input bg-background ring-offset-background data-[placeholder]:text-muted-foreground focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
22-
className,
23-
)}
24-
{...restProps}
25-
>
26-
{@render children?.()}
27-
{#if downIcon}
28-
<Icon icon={downIcon} class="size-4 opacity-50" />
29-
{/if}
30-
</SelectPrimitive.Trigger>
26+
<div class={cn('relative group', className)} {style}>
27+
<SelectPrimitive.Trigger
28+
bind:ref
29+
class={cn(
30+
'border-input bg-background ring-offset-background data-[placeholder]:text-muted-foreground focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
31+
// matches the Button 'outline' variant
32+
'hover:bg-accent hover:text-accent-foreground',
33+
)}
34+
{...restProps}
35+
>
36+
{@render children?.()}
37+
</SelectPrimitive.Trigger>
38+
<div class="absolute right-0 top-1/2 -translate-y-1/2 flex items-center gap-0.5 pointer-events-none">
39+
{#if onClear && !restProps.disabled}
40+
<XButton class="pointer-events-auto group-has-[[data-placeholder]]:hidden" onclick={onClear} />
41+
{/if}
42+
{#if downIcon}
43+
<Icon icon={downIcon} class="mr-2 size-5 shrink-0 opacity-50" />
44+
{/if}
45+
</div>
46+
</div>

frontend/viewer/src/lib/components/ui/switch/switch.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
bind:checked
2121
class={cn(
2222
'focus-visible:ring-ring focus-visible:ring-offset-background data-[state=checked]:bg-primary data-[state=unchecked]:bg-input peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
23-
className,
23+
!!label || className,
2424
)}
2525
{...restProps}
2626
>
@@ -35,7 +35,7 @@
3535
{/snippet}
3636

3737
{#if label}
38-
<Label class="cursor-pointer flex items-center gap-2">
38+
<Label class={cn('cursor-pointer flex items-center gap-4 max-md:w-full max-md:h-10', className)}>
3939
{@render control()}
4040
<span>{label}</span>
4141
</Label>

frontend/viewer/src/locales/en.po

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ msgstr "Add Word"
130130
msgid "an entry"
131131
msgstr "an entry"
132132

133+
#: src/project/browse/filter/SemanticDomainSelect.svelte
134+
msgid "Any semantic domain"
135+
msgstr "Any semantic domain"
136+
133137
#: src/project/browse/filter/WsSelect.svelte
134138
msgid "Any Ws"
135139
msgstr "Any Ws"
@@ -205,14 +209,15 @@ msgstr "Choose theme"
205209
msgid "Citation form"
206210
msgstr "Citation form"
207211

208-
#: src/project/browse/SearchFilter.svelte
212+
#: src/project/browse/filter/FieldSelect.svelte
209213
msgid "Citation Form"
210214
msgstr "Citation Form"
211215

212216
#: src/home/HomeView.svelte
213217
msgid "Classic FieldWorks Projects"
214218
msgstr "Classic FieldWorks Projects"
215219

220+
#: src/lib/components/field-editors/select.svelte
216221
#: src/lib/components/field-editors/select.svelte
217222
#: src/lib/components/field-editors/multi-select.svelte
218223
msgid "clear"
@@ -342,7 +347,7 @@ msgstr "Dictionary Preview"
342347
msgid "Discard"
343348
msgstr "Discard"
344349

345-
#: src/project/browse/SearchFilter.svelte
350+
#: src/project/browse/filter/FieldSelect.svelte
346351
#: src/lib/entry-editor/object-editors/EntryEditorPrimitive.svelte
347352
msgid "Display as"
348353
msgstr "Display as"
@@ -398,6 +403,10 @@ msgstr "Editor"
398403
msgid "Ends with"
399404
msgstr "Ends with"
400405

406+
#: src/project/browse/filter/MissingSelect.svelte
407+
msgid "Entries missing..."
408+
msgstr "Entries missing..."
409+
401410
#: src/project/browse/EntryMenu.svelte
402411
#: src/lib/entry-editor/EntryOrSensePicker.svelte
403412
#: src/lib/entry-editor/EditEntryDialog.svelte
@@ -464,7 +473,7 @@ msgstr "Failed to synchronize"
464473
msgid "Feedback & Support"
465474
msgstr "Feedback & Support"
466475

467-
#: src/project/browse/SearchFilter.svelte
476+
#: src/project/browse/filter/FieldSelect.svelte
468477
msgid "Field"
469478
msgstr "Field"
470479

@@ -526,8 +535,8 @@ msgid "Filter entries"
526535
msgstr "Filter entries"
527536

528537
#: src/project/browse/SearchFilter.svelte
529-
msgid "Filter for"
530-
msgstr "Filter for"
538+
msgid "Filter for..."
539+
msgstr "Filter for..."
531540

532541
#: src/project/browse/SearchFilter.svelte
533542
msgid "Filter words"
@@ -558,7 +567,7 @@ msgstr "For any other inquiries, feel free to send us an email."
558567
msgid "Get support"
559568
msgstr "Get support"
560569

561-
#: src/project/browse/SearchFilter.svelte
570+
#: src/project/browse/filter/FieldSelect.svelte
562571
#: src/lib/entry-editor/object-editors/SenseEditorPrimitive.svelte
563572
msgid "Gloss"
564573
msgstr "Gloss"
@@ -604,6 +613,14 @@ msgstr "Import"
604613
msgid "in {0}"
605614
msgstr "in {0}"
606615

616+
#: src/project/browse/SearchFilter.svelte
617+
msgid "Include subdomains"
618+
msgstr "Include subdomains"
619+
620+
#: src/project/browse/SearchFilter.svelte
621+
msgid "Incomplete entries"
622+
msgstr "Incomplete entries"
623+
607624
#: src/project/tasks/DoneView.svelte
608625
msgid "Keep going"
609626
msgstr "Keep going"
@@ -633,7 +650,7 @@ msgstr "Lexbox logo"
633650
msgid "Lexeme form"
634651
msgstr "Lexeme form"
635652

636-
#: src/project/browse/SearchFilter.svelte
653+
#: src/project/browse/filter/FieldSelect.svelte
637654
msgid "Lexeme Form"
638655
msgstr "Lexeme Form"
639656

@@ -711,26 +728,17 @@ msgstr "Missing Definition {0}"
711728
msgid "Missing Example sentence {0}"
712729
msgstr "Missing Example sentence {0}"
713730

714-
#: src/project/browse/SearchFilter.svelte
715-
msgid "Missing Examples"
716-
msgstr "Missing Examples"
717-
718731
#: src/project/tasks/tasks-service.ts
719732
msgid "Missing Gloss {0}"
720733
msgstr "Missing Gloss {0}"
721734

722735
#: src/project/tasks/tasks-service.ts
723-
#: src/project/browse/SearchFilter.svelte
724736
msgid "Missing Part of Speech"
725737
msgstr "Missing Part of Speech"
726738

727-
#: src/project/browse/SearchFilter.svelte
728-
msgid "Missing Semantic Domains"
729-
msgstr "Missing Semantic Domains"
730-
731-
#: src/project/browse/SearchFilter.svelte
732-
msgid "Missing Senses"
733-
msgstr "Missing Senses"
739+
#: src/project/browse/filter/MissingSelect.svelte
740+
msgid "Missing: {0}"
741+
msgstr "Missing: {0}"
734742

735743
#: src/lib/entry-editor/ItemListItem.svelte
736744
msgid "Move"
@@ -992,6 +1000,10 @@ msgstr "Select a new task to work on"
9921000
msgid "Select file"
9931001
msgstr "Select file"
9941002

1003+
#: src/project/browse/SearchFilter.svelte
1004+
msgid "Semantic domain"
1005+
msgstr "Semantic domain"
1006+
9951007
#: src/lib/entry-editor/object-editors/SenseEditorPrimitive.svelte
9961008
msgid "Semantic domains"
9971009
msgstr "Semantic domains"
@@ -1051,6 +1063,10 @@ msgstr "Size:"
10511063
msgid "Skip"
10521064
msgstr "Skip"
10531065

1066+
#: src/project/browse/SearchFilter.svelte
1067+
msgid "Specific field"
1068+
msgstr "Specific field"
1069+
10541070
#: src/project/browse/filter/OpFilter.svelte
10551071
msgid "Starts with"
10561072
msgstr "Starts with"
@@ -1260,8 +1276,8 @@ msgstr "Where are my projects?"
12601276
msgid "Why is that?"
12611277
msgstr "Why is that?"
12621278

1263-
#: src/project/browse/SearchFilter.svelte
12641279
#: src/project/browse/EntryMenu.svelte
1280+
#: src/project/browse/filter/FieldSelect.svelte
12651281
#: src/lib/entry-editor/EntryOrSensePicker.svelte
12661282
#: src/lib/entry-editor/EditEntryDialog.svelte
12671283
#: src/lib/entry-editor/object-editors/EntryEditorPrimitive.svelte

0 commit comments

Comments
 (0)