@eightyfourthousand/lib-search 2026.3.1 → 2026.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +4 -0
- package/index.js.map +1 -0
- package/lib/data/index.js +2 -0
- package/lib/data/index.js.map +1 -0
- package/lib/data/search.d.ts +5 -0
- package/lib/data/search.js +18 -0
- package/lib/data/search.js.map +1 -0
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/search.d.ts +71 -0
- package/lib/types/search.js +58 -0
- package/lib/types/search.js.map +1 -0
- package/lib/ui/SearchButton.d.ts +35 -0
- package/lib/ui/SearchButton.js +224 -0
- package/lib/ui/SearchButton.js.map +1 -0
- package/lib/ui/SearchResultCard.d.ts +26 -0
- package/lib/ui/SearchResultCard.js +67 -0
- package/lib/ui/SearchResultCard.js.map +1 -0
- package/lib/ui/SearchResultTab.d.ts +4 -0
- package/lib/ui/SearchResultTab.js +7 -0
- package/lib/ui/SearchResultTab.js.map +1 -0
- package/lib/ui/SearchResultsList.d.ts +8 -0
- package/lib/ui/SearchResultsList.js +10 -0
- package/lib/ui/SearchResultsList.js.map +1 -0
- package/lib/ui/index.js +2 -0
- package/lib/ui/index.js.map +1 -0
- package/package.json +19 -5
- package/.babelrc +0 -12
- package/.eslintrc.json +0 -18
- package/README.md +0 -9
- package/project.json +0 -20
- package/src/lib/data/search.ts +0 -28
- package/src/lib/types/search.ts +0 -140
- package/src/lib/ui/SearchButton.tsx +0 -151
- package/src/lib/ui/SearchResultCard.tsx +0 -117
- package/src/lib/ui/SearchResultTab.tsx +0 -28
- package/src/lib/ui/SearchResultsList.tsx +0 -25
- package/tsconfig.json +0 -17
- package/tsconfig.lib.json +0 -26
- /package/{src/index.ts → index.d.ts} +0 -0
- /package/{src/lib/data/index.ts → lib/data/index.d.ts} +0 -0
- /package/{src/lib/types/index.ts → lib/types/index.d.ts} +0 -0
- /package/{src/lib/ui/index.ts → lib/ui/index.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eightyfourthousand/lib-search",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.4.1",
|
|
4
4
|
"description": "Shared translation search UI and server helpers for 84000 applications.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"repository": {
|
|
@@ -17,16 +17,30 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@eightyfourthousand/
|
|
21
|
-
"@eightyfourthousand/
|
|
22
|
-
"@eightyfourthousand/
|
|
20
|
+
"@eightyfourthousand/client-graphql": "^2026.4.1",
|
|
21
|
+
"@eightyfourthousand/data-access": "^2026.4.1",
|
|
22
|
+
"@eightyfourthousand/design-system": "^2026.4.1",
|
|
23
|
+
"@eightyfourthousand/lib-utils": "^2026.4.1",
|
|
24
|
+
"lucide-react": "^0.477.0"
|
|
23
25
|
},
|
|
24
26
|
"peerDependencies": {
|
|
25
27
|
"next": "^16.0.0",
|
|
26
28
|
"react": "^19.0.0",
|
|
27
29
|
"react-dom": "^19.0.0"
|
|
28
30
|
},
|
|
31
|
+
"type": "module",
|
|
32
|
+
"main": "./index.js",
|
|
33
|
+
"module": "./index.js",
|
|
34
|
+
"types": "./index.d.ts",
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./index.d.ts",
|
|
38
|
+
"import": "./index.js",
|
|
39
|
+
"default": "./index.js"
|
|
40
|
+
},
|
|
41
|
+
"./package.json": "./package.json"
|
|
42
|
+
},
|
|
29
43
|
"sideEffects": false,
|
|
30
44
|
"keywords": [],
|
|
31
45
|
"author": ""
|
|
32
|
-
}
|
|
46
|
+
}
|
package/.babelrc
DELETED
package/.eslintrc.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
|
3
|
-
"ignorePatterns": ["!**/*"],
|
|
4
|
-
"overrides": [
|
|
5
|
-
{
|
|
6
|
-
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
7
|
-
"rules": {}
|
|
8
|
-
},
|
|
9
|
-
{
|
|
10
|
-
"files": ["*.ts", "*.tsx"],
|
|
11
|
-
"rules": {}
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"files": ["*.js", "*.jsx"],
|
|
15
|
-
"rules": {}
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
package/README.md
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# @eightyfourthousand/lib-search
|
|
2
|
-
|
|
3
|
-
Shared translation search UI and server helpers for 84000 applications.
|
|
4
|
-
|
|
5
|
-
Use this package for the cross-application translation search dialog, result rendering, and supporting search helpers.
|
|
6
|
-
|
|
7
|
-
## Running unit tests
|
|
8
|
-
|
|
9
|
-
Run `nx test lib-search` to execute the unit tests via [Jest](https://jestjs.io).
|
package/project.json
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "lib-search",
|
|
3
|
-
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
4
|
-
"sourceRoot": "libs/lib-search/src",
|
|
5
|
-
"projectType": "library",
|
|
6
|
-
"tags": [],
|
|
7
|
-
"targets": {
|
|
8
|
-
"build": {
|
|
9
|
-
"executor": "@nx/js:tsc",
|
|
10
|
-
"outputs": ["{options.outputPath}"],
|
|
11
|
-
"options": {
|
|
12
|
-
"outputPath": "dist/libs/lib-search",
|
|
13
|
-
"main": "libs/lib-search/src/index.ts",
|
|
14
|
-
"tsConfig": "libs/lib-search/tsconfig.lib.json",
|
|
15
|
-
"rootDir": "libs/lib-search/src",
|
|
16
|
-
"generateExportsField": true
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
package/src/lib/data/search.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
'use server';
|
|
2
|
-
|
|
3
|
-
import { createServerClient } from '@eightyfourthousand/data-access/ssr';
|
|
4
|
-
import { searchResultsFromDTO } from '../types';
|
|
5
|
-
|
|
6
|
-
export const search = async ({
|
|
7
|
-
text,
|
|
8
|
-
uuid,
|
|
9
|
-
toh,
|
|
10
|
-
}: {
|
|
11
|
-
text: string;
|
|
12
|
-
uuid: string;
|
|
13
|
-
toh: string;
|
|
14
|
-
}) => {
|
|
15
|
-
const client = await createServerClient();
|
|
16
|
-
const { data, error } = await client.rpc('translation_search', {
|
|
17
|
-
search_term: text,
|
|
18
|
-
work_uuid: uuid,
|
|
19
|
-
toh: toh,
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
if (error) {
|
|
23
|
-
console.error('Search error:', error);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return searchResultsFromDTO(data);
|
|
28
|
-
};
|
package/src/lib/types/search.ts
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
export const RESULTS_ENTITIES = [
|
|
2
|
-
'passages',
|
|
3
|
-
'alignments',
|
|
4
|
-
'bibliographies',
|
|
5
|
-
'glossaries',
|
|
6
|
-
] as const;
|
|
7
|
-
|
|
8
|
-
export type ResultsEntity = (typeof RESULTS_ENTITIES)[number];
|
|
9
|
-
|
|
10
|
-
export type BibliographySearchMatchDTO = {
|
|
11
|
-
bibliography_uuid: string;
|
|
12
|
-
content: string;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type GlossarySearchMatchDTO = {
|
|
16
|
-
glossary_uuid: string;
|
|
17
|
-
content: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type PassageSearchMatchDTO = {
|
|
21
|
-
passage_uuid: string;
|
|
22
|
-
type: ResultsEntity;
|
|
23
|
-
label: string;
|
|
24
|
-
content: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type AlignmentSearchMatchDTO = {
|
|
28
|
-
passage_uuid: string;
|
|
29
|
-
english_label: string;
|
|
30
|
-
english: string;
|
|
31
|
-
tibetan: string;
|
|
32
|
-
tibetan_volume_number: string;
|
|
33
|
-
tibetan_folio_number: string;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export type SearchMatch = {
|
|
37
|
-
type: string;
|
|
38
|
-
uuid: string;
|
|
39
|
-
content: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export type PassageMatch = SearchMatch & {
|
|
43
|
-
type: 'passage';
|
|
44
|
-
section: string;
|
|
45
|
-
label: string;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export type AlignmentMatch = SearchMatch & {
|
|
49
|
-
type: 'alignment';
|
|
50
|
-
section: 'translation';
|
|
51
|
-
label: string;
|
|
52
|
-
source: string;
|
|
53
|
-
volume: string;
|
|
54
|
-
folio: string;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
export type BibliographyMatch = SearchMatch & {
|
|
58
|
-
type: 'bibliography';
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export type GlossaryMatch = SearchMatch & {
|
|
62
|
-
type: 'glossary';
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export type SearchResultsDTO = {
|
|
66
|
-
passage_matches: PassageSearchMatchDTO[];
|
|
67
|
-
alignment_matches: AlignmentSearchMatchDTO[];
|
|
68
|
-
bibliography_matches: BibliographySearchMatchDTO[];
|
|
69
|
-
glossary_matches: GlossarySearchMatchDTO[];
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type SearchResult =
|
|
73
|
-
| SearchMatch
|
|
74
|
-
| PassageMatch
|
|
75
|
-
| AlignmentMatch
|
|
76
|
-
| BibliographyMatch
|
|
77
|
-
| GlossaryMatch;
|
|
78
|
-
|
|
79
|
-
export type SearchResults = {
|
|
80
|
-
passages: PassageMatch[];
|
|
81
|
-
alignments: AlignmentMatch[];
|
|
82
|
-
bibliographies: SearchMatch[];
|
|
83
|
-
glossaries: SearchMatch[];
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export const bibliographyMatchFromDTO = (
|
|
87
|
-
dto: BibliographySearchMatchDTO,
|
|
88
|
-
): BibliographyMatch => {
|
|
89
|
-
return {
|
|
90
|
-
type: 'bibliography',
|
|
91
|
-
uuid: dto.bibliography_uuid,
|
|
92
|
-
content: dto.content,
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export const glossaryMatchFromDTO = (
|
|
97
|
-
dto: GlossarySearchMatchDTO,
|
|
98
|
-
): GlossaryMatch => {
|
|
99
|
-
return {
|
|
100
|
-
type: 'glossary',
|
|
101
|
-
uuid: dto.glossary_uuid,
|
|
102
|
-
content: dto.content,
|
|
103
|
-
};
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
export const passageMatchFromDTO = (
|
|
107
|
-
dto: PassageSearchMatchDTO,
|
|
108
|
-
): PassageMatch => {
|
|
109
|
-
return {
|
|
110
|
-
type: 'passage',
|
|
111
|
-
uuid: dto.passage_uuid,
|
|
112
|
-
section: dto.type,
|
|
113
|
-
label: dto.label,
|
|
114
|
-
content: dto.content,
|
|
115
|
-
};
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
export const alignmentMatchFromDTO = (
|
|
119
|
-
dto: AlignmentSearchMatchDTO,
|
|
120
|
-
): AlignmentMatch => {
|
|
121
|
-
return {
|
|
122
|
-
type: 'alignment',
|
|
123
|
-
section: 'translation',
|
|
124
|
-
uuid: dto.passage_uuid,
|
|
125
|
-
label: dto.english_label,
|
|
126
|
-
content: dto.english,
|
|
127
|
-
source: dto.tibetan,
|
|
128
|
-
volume: dto.tibetan_volume_number,
|
|
129
|
-
folio: dto.tibetan_folio_number,
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
export const searchResultsFromDTO = (dto: SearchResultsDTO): SearchResults => {
|
|
134
|
-
return {
|
|
135
|
-
passages: dto.passage_matches.map(passageMatchFromDTO),
|
|
136
|
-
alignments: dto.alignment_matches.map(alignmentMatchFromDTO),
|
|
137
|
-
bibliographies: dto.bibliography_matches.map(bibliographyMatchFromDTO),
|
|
138
|
-
glossaries: dto.glossary_matches.map(glossaryMatchFromDTO),
|
|
139
|
-
};
|
|
140
|
-
};
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
Button,
|
|
5
|
-
Dialog,
|
|
6
|
-
DialogContent,
|
|
7
|
-
DialogDescription,
|
|
8
|
-
DialogTitle,
|
|
9
|
-
DialogTrigger,
|
|
10
|
-
Input,
|
|
11
|
-
Tabs,
|
|
12
|
-
TabsContent,
|
|
13
|
-
} from '@eightyfourthousand/design-system';
|
|
14
|
-
import { Loader2Icon, SearchIcon, XIcon } from 'lucide-react';
|
|
15
|
-
import { useEffect, useState } from 'react';
|
|
16
|
-
import { search } from '../data';
|
|
17
|
-
import { RESULTS_ENTITIES, SearchResult, SearchResults } from '../types';
|
|
18
|
-
import { SearchResultsList } from './SearchResultsList';
|
|
19
|
-
import { SearchResultTabs } from './SearchResultTab';
|
|
20
|
-
|
|
21
|
-
export const SearchButton = ({
|
|
22
|
-
workUuid,
|
|
23
|
-
toh,
|
|
24
|
-
onResultSelected,
|
|
25
|
-
}: {
|
|
26
|
-
workUuid?: string;
|
|
27
|
-
toh?: string;
|
|
28
|
-
onResultSelected: (result: SearchResult) => void;
|
|
29
|
-
}) => {
|
|
30
|
-
const [open, setOpen] = useState(false);
|
|
31
|
-
const [searching, setSearching] = useState(false);
|
|
32
|
-
const [hasResults, setHasResults] = useState(false);
|
|
33
|
-
const [searchQuery, setSearchQuery] = useState('');
|
|
34
|
-
const [results, setResults] = useState<SearchResults>();
|
|
35
|
-
|
|
36
|
-
const onCardClick = (result: SearchResult) => {
|
|
37
|
-
setOpen(false);
|
|
38
|
-
onResultSelected(result);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const performSearch = async () => {
|
|
43
|
-
if (!searchQuery || !workUuid || !toh) {
|
|
44
|
-
setResults(undefined);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
setSearching(true);
|
|
49
|
-
|
|
50
|
-
const results = await search({ text: searchQuery, uuid: workUuid, toh });
|
|
51
|
-
setHasResults(
|
|
52
|
-
!!results &&
|
|
53
|
-
(results.passages.length > 0 ||
|
|
54
|
-
results.alignments.length > 0 ||
|
|
55
|
-
results.bibliographies.length > 0 ||
|
|
56
|
-
results.glossaries.length > 0),
|
|
57
|
-
);
|
|
58
|
-
setResults(results);
|
|
59
|
-
setSearching(false);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const debounce = setTimeout(performSearch, 300);
|
|
63
|
-
return () => clearTimeout(debounce);
|
|
64
|
-
}, [searchQuery, workUuid, toh]);
|
|
65
|
-
|
|
66
|
-
useEffect(() => {
|
|
67
|
-
setResults(undefined);
|
|
68
|
-
setSearchQuery('');
|
|
69
|
-
}, [open]);
|
|
70
|
-
|
|
71
|
-
return (
|
|
72
|
-
<Dialog open={open} onOpenChange={setOpen}>
|
|
73
|
-
<DialogTrigger asChild>
|
|
74
|
-
<Button
|
|
75
|
-
variant="ghost"
|
|
76
|
-
className="bg-background my-auto [&_svg]:size-6 [&_svg]:stroke-1 hover:bg-background cursor-pointer text-base text-accent hover:text-accent/80"
|
|
77
|
-
>
|
|
78
|
-
<SearchIcon />
|
|
79
|
-
<span className="hidden md:flex">Search</span>
|
|
80
|
-
</Button>
|
|
81
|
-
</DialogTrigger>
|
|
82
|
-
<DialogContent
|
|
83
|
-
showCloseButton={false}
|
|
84
|
-
className="bg-transparent top-4 max-w-4xl shadow-none border-0 text-secondary translate-y-0"
|
|
85
|
-
>
|
|
86
|
-
<DialogTitle className="hidden">Search</DialogTitle>
|
|
87
|
-
<DialogDescription className="hidden">
|
|
88
|
-
Search this translation
|
|
89
|
-
</DialogDescription>
|
|
90
|
-
<div className="flex flex-col justify-start gap-2 h-[calc(100vh_-_2.5rem)]">
|
|
91
|
-
<div className="w-full flex flex-col gap-2 text-foreground flex-shrink-0">
|
|
92
|
-
<div className="flex justify-end">
|
|
93
|
-
<Button
|
|
94
|
-
className="text-secondary-foreground -me-3"
|
|
95
|
-
variant="ghost"
|
|
96
|
-
size="icon"
|
|
97
|
-
onClick={() => setOpen(false)}
|
|
98
|
-
>
|
|
99
|
-
<XIcon className="size-3 my-auto" />
|
|
100
|
-
</Button>
|
|
101
|
-
</div>
|
|
102
|
-
<Input
|
|
103
|
-
autoFocus
|
|
104
|
-
placeholder="Type to search..."
|
|
105
|
-
className="w-full text-foreground px-4 py-6"
|
|
106
|
-
onChange={(e) => setSearchQuery(e.target.value)}
|
|
107
|
-
/>
|
|
108
|
-
</div>
|
|
109
|
-
{searchQuery && (
|
|
110
|
-
<>
|
|
111
|
-
<div className="text-sm text-secondary-foreground">
|
|
112
|
-
Showing results for "<strong>{searchQuery}</strong>"
|
|
113
|
-
{searching && (
|
|
114
|
-
<Loader2Icon className="size-4 ml-2 animate-spin inline-block" />
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
{results && hasResults ? (
|
|
118
|
-
<Tabs
|
|
119
|
-
defaultValue="passages"
|
|
120
|
-
className="flex flex-col grow min-h-0 pt-2"
|
|
121
|
-
>
|
|
122
|
-
<SearchResultTabs results={results} />
|
|
123
|
-
{RESULTS_ENTITIES.map(
|
|
124
|
-
(tab) =>
|
|
125
|
-
results[tab].length > 0 && (
|
|
126
|
-
<TabsContent
|
|
127
|
-
key={tab}
|
|
128
|
-
className="grow min-h-0"
|
|
129
|
-
value={tab}
|
|
130
|
-
>
|
|
131
|
-
<SearchResultsList
|
|
132
|
-
query={searchQuery}
|
|
133
|
-
results={results[tab]}
|
|
134
|
-
onCardClick={onCardClick}
|
|
135
|
-
/>
|
|
136
|
-
</TabsContent>
|
|
137
|
-
),
|
|
138
|
-
)}
|
|
139
|
-
</Tabs>
|
|
140
|
-
) : (
|
|
141
|
-
<div className="mt-4 text-secondary-foreground">
|
|
142
|
-
No results found.
|
|
143
|
-
</div>
|
|
144
|
-
)}
|
|
145
|
-
</>
|
|
146
|
-
)}
|
|
147
|
-
</div>
|
|
148
|
-
</DialogContent>
|
|
149
|
-
</Dialog>
|
|
150
|
-
);
|
|
151
|
-
};
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { highlightText, removeHtmlTags, useIsMobile } from '@eightyfourthousand/lib-utils';
|
|
4
|
-
import {
|
|
5
|
-
AlignmentMatch,
|
|
6
|
-
BibliographyMatch,
|
|
7
|
-
GlossaryMatch,
|
|
8
|
-
PassageMatch,
|
|
9
|
-
SearchResult,
|
|
10
|
-
} from '../types';
|
|
11
|
-
import { Separator } from '@eightyfourthousand/design-system';
|
|
12
|
-
|
|
13
|
-
export const PassageResult = ({
|
|
14
|
-
match,
|
|
15
|
-
query,
|
|
16
|
-
}: {
|
|
17
|
-
match: PassageMatch;
|
|
18
|
-
query: string;
|
|
19
|
-
}) => {
|
|
20
|
-
return <div>{highlightText(match.content, query)}</div>;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const AlignmentResult = ({
|
|
24
|
-
match,
|
|
25
|
-
query,
|
|
26
|
-
}: {
|
|
27
|
-
match: AlignmentMatch;
|
|
28
|
-
query: string;
|
|
29
|
-
}) => {
|
|
30
|
-
return (
|
|
31
|
-
<div className="grid md:grid-cols-2 gap-4">
|
|
32
|
-
<div>{highlightText(match.content, query)}</div>
|
|
33
|
-
<div className="text-lg">{highlightText(match.source, query)}</div>
|
|
34
|
-
</div>
|
|
35
|
-
);
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const BibliographyResult = ({
|
|
39
|
-
match,
|
|
40
|
-
query,
|
|
41
|
-
}: {
|
|
42
|
-
match: BibliographyMatch;
|
|
43
|
-
query: string;
|
|
44
|
-
}) => {
|
|
45
|
-
return <div>{highlightText(removeHtmlTags(match.content), query)}</div>;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const GlossaryResult = ({
|
|
49
|
-
match,
|
|
50
|
-
query,
|
|
51
|
-
}: {
|
|
52
|
-
match: GlossaryMatch;
|
|
53
|
-
query: string;
|
|
54
|
-
}) => {
|
|
55
|
-
return <div>{highlightText(removeHtmlTags(match.content), query)}</div>;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export const SearchResultCard = ({
|
|
59
|
-
match,
|
|
60
|
-
query,
|
|
61
|
-
onClick,
|
|
62
|
-
}: {
|
|
63
|
-
match: SearchResult;
|
|
64
|
-
query: string;
|
|
65
|
-
onClick: () => void;
|
|
66
|
-
}) => {
|
|
67
|
-
const isMobile = useIsMobile();
|
|
68
|
-
|
|
69
|
-
// cast as passage but treat properties as optional
|
|
70
|
-
const passage = match as PassageMatch;
|
|
71
|
-
|
|
72
|
-
const renderInner = (match: SearchResult) => {
|
|
73
|
-
switch (match.type) {
|
|
74
|
-
case 'passage':
|
|
75
|
-
return <PassageResult match={match as PassageMatch} query={query} />;
|
|
76
|
-
case 'alignment':
|
|
77
|
-
return (
|
|
78
|
-
<AlignmentResult match={match as AlignmentMatch} query={query} />
|
|
79
|
-
);
|
|
80
|
-
case 'bibliography':
|
|
81
|
-
return (
|
|
82
|
-
<BibliographyResult
|
|
83
|
-
match={match as BibliographyMatch}
|
|
84
|
-
query={query}
|
|
85
|
-
/>
|
|
86
|
-
);
|
|
87
|
-
case 'glossary':
|
|
88
|
-
return <GlossaryResult match={match as GlossaryMatch} query={query} />;
|
|
89
|
-
default:
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
return (
|
|
95
|
-
<div
|
|
96
|
-
onClick={onClick}
|
|
97
|
-
className="flex flex-col md:flex-row gap-4 py-6 px-4 md:px-6 font-serif text-sm text-foreground rounded-lg bg-background cursor-pointer border border-3 border-transparent hover:border-border transition-colors"
|
|
98
|
-
>
|
|
99
|
-
<div className="md:w-25 flex-shrink-0 flex md:flex-col gap-1 md:gap-2 md:text-right text-xs md:text-sm">
|
|
100
|
-
<div className="flex flex-col gap-1">
|
|
101
|
-
<span className="text-secondary capitalize">{passage.type}</span>
|
|
102
|
-
{passage.section && (
|
|
103
|
-
<span className="text-foreground/50 capitalize">
|
|
104
|
-
{passage.section.replace('Header', '')}
|
|
105
|
-
</span>
|
|
106
|
-
)}
|
|
107
|
-
</div>
|
|
108
|
-
<div className="grow" />
|
|
109
|
-
{passage.label && (
|
|
110
|
-
<span className="text-accent mt-auto">{passage.label}</span>
|
|
111
|
-
)}
|
|
112
|
-
</div>
|
|
113
|
-
<Separator orientation={isMobile ? 'horizontal' : 'vertical'} />
|
|
114
|
-
{renderInner(match)}
|
|
115
|
-
</div>
|
|
116
|
-
);
|
|
117
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { TabsList, TabsTrigger } from '@eightyfourthousand/design-system';
|
|
2
|
-
import { RESULTS_ENTITIES, SearchResults } from '../types';
|
|
3
|
-
|
|
4
|
-
export const SearchResultTabs = ({ results }: { results: SearchResults }) => {
|
|
5
|
-
return (
|
|
6
|
-
<TabsList className="h-24 gap-4 flex-shrink-0 rounded-lg p-0 w-full bg-transparent">
|
|
7
|
-
{RESULTS_ENTITIES.map(
|
|
8
|
-
(tab) =>
|
|
9
|
-
results[tab].length > 0 && (
|
|
10
|
-
<TabsTrigger
|
|
11
|
-
key={tab}
|
|
12
|
-
value={tab}
|
|
13
|
-
className="h-full border border-border rounded-xl flex-grow data-[state=inactive]:bg-muted data-[state=active]:border-border data-[state=active]:border-3 border-2"
|
|
14
|
-
>
|
|
15
|
-
<div className="flex flex-col gap-1 items-center">
|
|
16
|
-
<span className="capitalize text-secondary font-bold">
|
|
17
|
-
{tab}
|
|
18
|
-
</span>
|
|
19
|
-
<span className="text-primary font-bold">
|
|
20
|
-
{results[tab].length} matches
|
|
21
|
-
</span>
|
|
22
|
-
</div>
|
|
23
|
-
</TabsTrigger>
|
|
24
|
-
),
|
|
25
|
-
)}
|
|
26
|
-
</TabsList>
|
|
27
|
-
);
|
|
28
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { SearchResult } from '../types';
|
|
2
|
-
import { SearchResultCard } from './SearchResultCard';
|
|
3
|
-
|
|
4
|
-
export const SearchResultsList = ({
|
|
5
|
-
query,
|
|
6
|
-
results,
|
|
7
|
-
onCardClick,
|
|
8
|
-
}: {
|
|
9
|
-
query: string;
|
|
10
|
-
results: SearchResult[];
|
|
11
|
-
onCardClick: (result: SearchResult) => void;
|
|
12
|
-
}) => {
|
|
13
|
-
return (
|
|
14
|
-
<div className="h-full flex flex-col gap-3 pb-4 overflow-y-auto min-h-0">
|
|
15
|
-
{results.map((result, index) => (
|
|
16
|
-
<SearchResultCard
|
|
17
|
-
key={index}
|
|
18
|
-
match={result}
|
|
19
|
-
query={query}
|
|
20
|
-
onClick={() => onCardClick(result)}
|
|
21
|
-
/>
|
|
22
|
-
))}
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
};
|
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"jsx": "react-jsx",
|
|
4
|
-
"allowJs": false,
|
|
5
|
-
"esModuleInterop": false,
|
|
6
|
-
"allowSyntheticDefaultImports": true,
|
|
7
|
-
"strict": true
|
|
8
|
-
},
|
|
9
|
-
"files": [],
|
|
10
|
-
"include": [],
|
|
11
|
-
"references": [
|
|
12
|
-
{
|
|
13
|
-
"path": "./tsconfig.lib.json"
|
|
14
|
-
}
|
|
15
|
-
],
|
|
16
|
-
"extends": "../../tsconfig.base.json"
|
|
17
|
-
}
|
package/tsconfig.lib.json
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "./tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "../../dist/out-tsc",
|
|
5
|
-
"declaration": true,
|
|
6
|
-
"types": [
|
|
7
|
-
"node",
|
|
8
|
-
"@nx/react/typings/cssmodule.d.ts",
|
|
9
|
-
"@nx/react/typings/image.d.ts",
|
|
10
|
-
"next",
|
|
11
|
-
"@nx/next/typings/image.d.ts"
|
|
12
|
-
]
|
|
13
|
-
},
|
|
14
|
-
"exclude": [
|
|
15
|
-
"jest.config.ts",
|
|
16
|
-
"src/**/*.spec.ts",
|
|
17
|
-
"src/**/*.test.ts",
|
|
18
|
-
"src/**/*.spec.tsx",
|
|
19
|
-
"src/**/*.test.tsx",
|
|
20
|
-
"src/**/*.spec.js",
|
|
21
|
-
"src/**/*.test.js",
|
|
22
|
-
"src/**/*.spec.jsx",
|
|
23
|
-
"src/**/*.test.jsx"
|
|
24
|
-
],
|
|
25
|
-
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
|
26
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|