|
13 | 13 | import {useCurrentView} from '$lib/views/view-service'; |
14 | 14 | import {formatNumber} from '$lib/components/ui/format'; |
15 | 15 | import ViewT from '$lib/views/ViewT.svelte'; |
| 16 | + import Select from '$lib/components/field-editors/select.svelte'; |
| 17 | + import { Input } from '$lib/components/ui/input'; |
| 18 | + import {watch} from 'runed'; |
| 19 | + import OpFilter, {type Op} from './filter/OpFilter.svelte'; |
| 20 | + import WsSelect from './filter/WsSelect.svelte'; |
| 21 | + import {useWritingSystemService} from '$lib/writing-system-service.svelte'; |
16 | 22 |
|
17 | 23 | const stats = useProjectStats(); |
18 | 24 | const currentView = useCurrentView(); |
| 25 | + const wsService = useWritingSystemService(); |
19 | 26 |
|
20 | 27 | let { |
21 | 28 | search = $bindable(), |
|
28 | 35 | let missingSenses = $state(false); |
29 | 36 | let missingPartOfSpeech = $state(false); |
30 | 37 | let missingSemanticDomains = $state(false); |
| 38 | +
|
| 39 | + let fields: Array<{id: string, label: string, ws: 'vernacular-no-audio' | 'analysis-no-audio'}> = $derived([ |
| 40 | + { id: 'lexemeForm', label: pt($t`Lexeme Form`, $t`Word`, $currentView), ws: 'vernacular-no-audio' }, |
| 41 | + { id: 'citationForm', label: pt($t`Citation Form`, $t`Display as`, $currentView), ws: 'vernacular-no-audio' }, |
| 42 | + { id: 'senses.gloss', label: $t`Gloss`, ws: 'analysis-no-audio' }, |
| 43 | + ]); |
| 44 | +
|
| 45 | + // svelte-ignore state_referenced_locally |
| 46 | + let selectedField = $state(fields[0]); |
| 47 | + let selectedWs = $state<string[]>(wsService.vernacularNoAudio.map(ws => ws.wsId)); |
| 48 | + watch(() => fields, fields => { |
| 49 | + //updates selected field when selected view changes |
| 50 | + selectedField = fields.find(f => f.id === selectedField.id) ?? fields[0]; |
| 51 | + }); |
| 52 | + let fieldFilterValue = $state(''); |
| 53 | + let filterOp = $state<Op>('contains') |
| 54 | +
|
31 | 55 | $effect(() => { |
32 | 56 | let newFilter: string[] = []; |
33 | 57 | if (missingExamples) { |
|
42 | 66 | if (missingSemanticDomains) { |
43 | 67 | newFilter.push('Senses.SemanticDomains=null') |
44 | 68 | } |
| 69 | + if (fieldFilterValue && selectedWs?.length > 0) { |
| 70 | + let op: string; |
| 71 | + switch (filterOp) { |
| 72 | + case 'starts-with': op = '^'; break; |
| 73 | + case 'contains': op = '=*'; break; |
| 74 | + case 'ends-with': op = '$'; break; |
| 75 | + case 'equals': op = '='; break; |
| 76 | + case 'not-equals': op = '!='; break; |
| 77 | + } |
| 78 | + let fieldFilter = []; |
| 79 | + let escapedValue = escapeGridifyValue(fieldFilterValue); |
| 80 | + for (let ws of selectedWs) { |
| 81 | + fieldFilter.push(`${selectedField.id}[${ws}]${op}${escapedValue}`); |
| 82 | + } |
| 83 | + //construct a filter like LexemeForm[en]=value|LexemeForm[fr]=value |
| 84 | + newFilter.push('(' + fieldFilter.join('|') + ')') |
| 85 | + } |
45 | 86 | gridifyFilter = newFilter.join(', '); |
46 | 87 | }); |
47 | 88 |
|
| 89 | +
|
| 90 | + function escapeGridifyValue(v: string) { |
| 91 | + //from https://alirezanet.github.io/Gridify/guide/filtering#escaping |
| 92 | + return v.replace(/([(),|\\]|\/i)/g, '\\$1'); |
| 93 | + } |
| 94 | +
|
48 | 95 | let filtersExpanded = $state(false); |
49 | 96 | </script> |
50 | 97 |
|
|
78 | 125 | </ComposableInput> |
79 | 126 | </div> |
80 | 127 | <Collapsible.Content class="p-2 mb-2 space-y-2"> |
| 128 | + <div class="flex flex-col @md/list:flex-row gap-2 items-stretch"> |
| 129 | + <div class="flex flex-row gap-2 flex-1"> |
| 130 | + <!-- Field Picker --> |
| 131 | + <Select |
| 132 | + bind:value={selectedField} |
| 133 | + options={fields} |
| 134 | + idSelector="id" |
| 135 | + labelSelector="label" |
| 136 | + placeholder={$t`Field`} |
| 137 | + class="flex-1" |
| 138 | + /> |
| 139 | + <!-- Writing System Picker --> |
| 140 | + <WsSelect bind:value={selectedWs} wsType={selectedField.ws} /> |
| 141 | + </div> |
| 142 | + <!-- Text Box: on mobile, wraps to new line --> |
| 143 | + <div class="flex flex-row gap-2 flex-1"> |
| 144 | + <OpFilter bind:value={filterOp}/> |
| 145 | + <Input |
| 146 | + bind:value={fieldFilterValue} |
| 147 | + placeholder={$t`Filter for`} |
| 148 | + class="flex-1" |
| 149 | + /> |
| 150 | + </div> |
| 151 | + </div> |
81 | 152 | <Switch bind:checked={missingExamples} label={$t`Missing Examples`} /> |
82 | 153 | <Switch bind:checked={missingSenses} label={$t`Missing Senses`} /> |
83 | 154 | <Switch bind:checked={missingPartOfSpeech} label={$t`Missing Part of Speech`} /> |
|
0 commit comments