geonetwork-ui 2.9.0-dev.6da5cd143 → 2.9.0-dev.af124b7cd
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/fesm2022/geonetwork-ui.mjs +101 -28
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +11 -2
- package/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.html +8 -10
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +13 -3
- package/src/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.ts +2 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.html +1 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +35 -4
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
- package/src/libs/feature/router/src/lib/default/services/router-search.service.ts +15 -3
- package/src/libs/feature/router/src/lib/default/state/router.effects.ts +40 -4
- package/src/libs/feature/router/src/lib/default/state/router.facade.ts +2 -0
- package/src/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.ts +1 -0
- package/src/libs/feature/search/src/lib/state/reducer.ts +4 -0
- package/src/libs/feature/search/src/lib/utils/service/search.service.ts +2 -0
- package/src/libs/ui/elements/src/lib/image-input/image-input.component.html +5 -2
- package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +63 -56
- package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.scss +5 -5
- package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.html +19 -16
|
@@ -231,7 +231,8 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
231
231
|
return compatibleLinks[0]
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
|
-
})
|
|
234
|
+
}),
|
|
235
|
+
shareReplay(1)
|
|
235
236
|
)
|
|
236
237
|
|
|
237
238
|
isWmsStyleMode$ = this.selectedSourceLink$.pipe(
|
|
@@ -346,12 +347,38 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
346
347
|
shareReplay(1)
|
|
347
348
|
)
|
|
348
349
|
|
|
350
|
+
wmsMimeType$ = this.selectedSourceLink$.pipe(
|
|
351
|
+
switchMap((link) => {
|
|
352
|
+
if (link?.type === 'service' && link?.accessServiceProtocol === 'wms') {
|
|
353
|
+
return from(
|
|
354
|
+
new WmsEndpoint(link.url.toString())
|
|
355
|
+
.isReady()
|
|
356
|
+
.then((endpoint) => {
|
|
357
|
+
return endpoint.describeLayer(link.name).then((description) => {
|
|
358
|
+
if (description) {
|
|
359
|
+
return description.owsType === 'wfs'
|
|
360
|
+
? 'image/png'
|
|
361
|
+
: 'image/jpeg'
|
|
362
|
+
}
|
|
363
|
+
const layer = endpoint.getLayerByName(link.name)
|
|
364
|
+
return layer?.opaque ? 'image/jpeg' : 'image/png'
|
|
365
|
+
})
|
|
366
|
+
})
|
|
367
|
+
.catch(() => 'image/png')
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
return of('')
|
|
371
|
+
}),
|
|
372
|
+
shareReplay(1)
|
|
373
|
+
)
|
|
374
|
+
|
|
349
375
|
currentLayers$ = combineLatest([
|
|
350
376
|
this.selectedLink$,
|
|
351
377
|
this.excludeWfs$,
|
|
352
378
|
this.selectedWmsStyleName$,
|
|
379
|
+
this.wmsMimeType$,
|
|
353
380
|
]).pipe(
|
|
354
|
-
switchMap(([link, excludeWfs, wmsStyleName]) => {
|
|
381
|
+
switchMap(([link, excludeWfs, wmsStyleName, wmsMimeType]) => {
|
|
355
382
|
if (!link) {
|
|
356
383
|
return of([])
|
|
357
384
|
}
|
|
@@ -367,8 +394,12 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
367
394
|
}
|
|
368
395
|
return this.getLayerFromLink(link).pipe(
|
|
369
396
|
map((layer) =>
|
|
370
|
-
|
|
371
|
-
? {
|
|
397
|
+
layer.type === 'wms'
|
|
398
|
+
? {
|
|
399
|
+
...layer,
|
|
400
|
+
...(wmsStyleName && { style: wmsStyleName }),
|
|
401
|
+
...(wmsMimeType && { format: wmsMimeType }),
|
|
402
|
+
}
|
|
372
403
|
: layer
|
|
373
404
|
),
|
|
374
405
|
map((layer) => [layer]),
|
|
@@ -164,7 +164,7 @@ export class MdViewFacade {
|
|
|
164
164
|
link.accessServiceProtocol === 'ogcFeatures'
|
|
165
165
|
) {
|
|
166
166
|
return from(
|
|
167
|
-
this.dataService.getItemsFromOgcApi(link.url.href)
|
|
167
|
+
this.dataService.getItemsFromOgcApi(link.url.href, 1)
|
|
168
168
|
).pipe(
|
|
169
169
|
map((collectionRecords: OgcApiRecord[]) => {
|
|
170
170
|
return collectionRecords &&
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inject, Injectable } from '@angular/core'
|
|
2
2
|
import {
|
|
3
3
|
FieldsService,
|
|
4
4
|
SearchFacade,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from '../../../../../../../libs/feature/search/src'
|
|
7
7
|
import {
|
|
8
8
|
FieldFilters,
|
|
9
|
+
SortByEnum,
|
|
9
10
|
SortByField,
|
|
10
11
|
} from '../../../../../../../libs/common/domain/src/lib/model/search'
|
|
11
12
|
import { ROUTE_PARAMS, SearchRouteParams } from '../constants'
|
|
@@ -20,6 +21,7 @@ export class RouterSearchService implements SearchServiceI {
|
|
|
20
21
|
private fieldsService = inject(FieldsService)
|
|
21
22
|
|
|
22
23
|
setSortAndFilters(filters: FieldFilters, sortBy: SortByField) {
|
|
24
|
+
console.log('RouterSearchService - setSortAndFilters', { filters, sortBy })
|
|
23
25
|
this.fieldsService
|
|
24
26
|
.readFieldValuesFromFilters(filters)
|
|
25
27
|
.subscribe((values) => {
|
|
@@ -31,10 +33,12 @@ export class RouterSearchService implements SearchServiceI {
|
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
async setFilters(newFilters: FieldFilters) {
|
|
34
|
-
|
|
36
|
+
let sortBy = await firstValueFrom(this.searchFacade.sortBy$)
|
|
35
37
|
const fieldSearchParams = await firstValueFrom(
|
|
36
38
|
this.fieldsService.readFieldValuesFromFilters(newFilters)
|
|
37
39
|
)
|
|
40
|
+
// apply relevancy sort if a full text criteria is given
|
|
41
|
+
sortBy = newFilters['any'] ? SortByEnum.RELEVANCY : sortBy
|
|
38
42
|
this.facade.setSearch({
|
|
39
43
|
...fieldSearchParams,
|
|
40
44
|
[ROUTE_PARAMS.SORT]: sortBy ? sortByToString(sortBy) : undefined,
|
|
@@ -42,13 +46,21 @@ export class RouterSearchService implements SearchServiceI {
|
|
|
42
46
|
}
|
|
43
47
|
|
|
44
48
|
async updateFilters(newFilters: FieldFilters) {
|
|
49
|
+
console.log('RouterSearchService - updateFilters', newFilters)
|
|
45
50
|
const currentFilters = await firstValueFrom(
|
|
46
51
|
this.searchFacade.searchFilters$
|
|
47
52
|
)
|
|
48
53
|
const updatedFilters = { ...currentFilters, ...newFilters }
|
|
49
|
-
|
|
54
|
+
let newParams = await firstValueFrom(
|
|
50
55
|
this.fieldsService.readFieldValuesFromFilters(updatedFilters)
|
|
51
56
|
)
|
|
57
|
+
if (newFilters['any']) {
|
|
58
|
+
newParams = {
|
|
59
|
+
...newParams,
|
|
60
|
+
[ROUTE_PARAMS.SORT]: sortByToString(SortByEnum.RELEVANCY),
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
console.log('RouterSearchService - updateFilters - newParams', newParams)
|
|
52
64
|
this.facade.updateSearch(newParams as SearchRouteParams)
|
|
53
65
|
}
|
|
54
66
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Location } from '@angular/common'
|
|
2
|
-
import {
|
|
2
|
+
import { inject, Injectable } from '@angular/core'
|
|
3
3
|
import { ActivatedRouteSnapshot, Router } from '@angular/router'
|
|
4
4
|
import { MdViewActions } from '../../../../../../../libs/feature/record/src'
|
|
5
5
|
import {
|
|
@@ -9,15 +9,18 @@ import {
|
|
|
9
9
|
SetFilters,
|
|
10
10
|
SetSortBy,
|
|
11
11
|
} from '../../../../../../../libs/feature/search/src'
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
FieldFilters,
|
|
14
|
+
SortByEnum,
|
|
15
|
+
} from '../../../../../../../libs/common/domain/src/lib/model/search'
|
|
13
16
|
import { Actions, createEffect, ofType } from '@ngrx/effects'
|
|
14
17
|
import { navigation } from '@ngrx/router-store/data-persistence'
|
|
15
18
|
import { of, pairwise, startWith } from 'rxjs'
|
|
16
|
-
import { map, mergeMap, tap } from 'rxjs/operators'
|
|
19
|
+
import { map, mergeMap, take, tap } from 'rxjs/operators'
|
|
17
20
|
import * as RouterActions from './router.actions'
|
|
18
21
|
import { RouterFacade } from './router.facade'
|
|
19
22
|
import { ROUTE_PARAMS } from '../constants'
|
|
20
|
-
import { sortByFromString } from '../../../../../../../libs/util/shared/src'
|
|
23
|
+
import { sortByFromString, sortByToString } from '../../../../../../../libs/util/shared/src'
|
|
21
24
|
import { ROUTER_CONFIG, RouterConfigModel } from '../router.config'
|
|
22
25
|
import { RouterService } from '../router.service'
|
|
23
26
|
|
|
@@ -55,6 +58,12 @@ export class RouterEffects {
|
|
|
55
58
|
startWith([null, {}] as [Record<string, string>, FieldFilters]),
|
|
56
59
|
pairwise(),
|
|
57
60
|
map(([[oldParams, oldFilters], [newParams, newFilters]]) => {
|
|
61
|
+
console.log('RouterEffects - syncSearchState', {
|
|
62
|
+
oldParams,
|
|
63
|
+
newParams,
|
|
64
|
+
oldFilters,
|
|
65
|
+
newFilters,
|
|
66
|
+
})
|
|
58
67
|
let sortBy =
|
|
59
68
|
ROUTE_PARAMS.SORT in newParams
|
|
60
69
|
? sortByFromString(newParams[ROUTE_PARAMS.SORT])
|
|
@@ -103,6 +112,33 @@ export class RouterEffects {
|
|
|
103
112
|
)
|
|
104
113
|
)
|
|
105
114
|
|
|
115
|
+
/**
|
|
116
|
+
* This effect is needed because on the page load, the search params from the URL are
|
|
117
|
+
* directly applied to the underlying search facade; this means that it doesn't go
|
|
118
|
+
* through the RouterSearchService which makes sure that a relevancy sort is applied
|
|
119
|
+
* whenever there's a full text search criteria set.
|
|
120
|
+
*/
|
|
121
|
+
applyInitialRelevancySort$ = createEffect(
|
|
122
|
+
() =>
|
|
123
|
+
this.facade.searchParams$.pipe(
|
|
124
|
+
take(1),
|
|
125
|
+
tap((filters) => {
|
|
126
|
+
const relevancySort = sortByToString(SortByEnum.RELEVANCY)
|
|
127
|
+
if (
|
|
128
|
+
filters['q'] &&
|
|
129
|
+
(!Array.isArray(filters['q']) || filters['q'].length > 0) &&
|
|
130
|
+
!filters[ROUTE_PARAMS.SORT]
|
|
131
|
+
) {
|
|
132
|
+
this.facade.updateSearch({
|
|
133
|
+
...filters,
|
|
134
|
+
[ROUTE_PARAMS.SORT]: relevancySort,
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
})
|
|
138
|
+
),
|
|
139
|
+
{ dispatch: false }
|
|
140
|
+
)
|
|
141
|
+
|
|
106
142
|
/**
|
|
107
143
|
* This effect will load the metadata when a navigation to
|
|
108
144
|
* a metadata record happens
|
|
@@ -70,6 +70,7 @@ export class RouterFacade {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
updateSearch(query?: SearchRouteParams) {
|
|
73
|
+
console.log('RouterFacade - updateSearch', query)
|
|
73
74
|
this.go({
|
|
74
75
|
path: this.routerService.getSearchRoute(),
|
|
75
76
|
...(query && { query: flattenQueryParams(query) }),
|
|
@@ -78,6 +79,7 @@ export class RouterFacade {
|
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
setSearch(query?: SearchRouteParams) {
|
|
82
|
+
console.log('RouterFacade - setSearch', query)
|
|
81
83
|
this.go({
|
|
82
84
|
path: this.routerService.getSearchRoute(),
|
|
83
85
|
...(query && { query: flattenQueryParams(query) }),
|
|
@@ -79,6 +79,7 @@ export class FuzzySearchComponent implements OnInit {
|
|
|
79
79
|
if (this.inputSubmitted.observers.length > 0) {
|
|
80
80
|
this.inputSubmitted.emit(any)
|
|
81
81
|
} else {
|
|
82
|
+
console.log('FuzzySearchComponent - handleInputSubmission', any)
|
|
82
83
|
this.searchService.updateFilters({ any })
|
|
83
84
|
}
|
|
84
85
|
}
|
|
@@ -111,7 +111,10 @@ export function reducerSearch(
|
|
|
111
111
|
},
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
+
// From router.effects
|
|
115
|
+
// From home - fuzzy-search - search.service
|
|
114
116
|
case fromActions.SET_FILTERS: {
|
|
117
|
+
console.log('reducerSearch - SET_FILTERS', action.payload)
|
|
115
118
|
return {
|
|
116
119
|
...state,
|
|
117
120
|
params: {
|
|
@@ -132,6 +135,7 @@ export function reducerSearch(
|
|
|
132
135
|
},
|
|
133
136
|
}
|
|
134
137
|
}
|
|
138
|
+
// From results WC
|
|
135
139
|
case fromActions.SET_SEARCH: {
|
|
136
140
|
return {
|
|
137
141
|
...state,
|
|
@@ -19,11 +19,13 @@ export class SearchService implements SearchServiceI {
|
|
|
19
19
|
private facade = inject(SearchFacade)
|
|
20
20
|
|
|
21
21
|
setSortAndFilters(filters: FieldFilters, sort: SortByField) {
|
|
22
|
+
console.log('SearchService - setSortAndFilters', { filters, sort })
|
|
22
23
|
this.setFilters(filters)
|
|
23
24
|
this.setSortBy(sort)
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
updateFilters(params: FieldFilters) {
|
|
28
|
+
console.log('SearchService - updateFilters', params)
|
|
27
29
|
this.facade.searchFilters$
|
|
28
30
|
.pipe(
|
|
29
31
|
first(),
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
@if (previewUrl) {
|
|
2
|
-
<div class="w-
|
|
3
|
-
<gn-ui-image-overlay-preview
|
|
2
|
+
<div class="w-[314px] h-full flex flex-col gap-2">
|
|
3
|
+
<gn-ui-image-overlay-preview
|
|
4
|
+
class="w-[314px] h-[314px]"
|
|
5
|
+
[imageUrl]="previewUrl"
|
|
6
|
+
>
|
|
4
7
|
</gn-ui-image-overlay-preview>
|
|
5
8
|
@if (showAltTextInput) {
|
|
6
9
|
<gn-ui-text-input
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
</div>
|
|
15
15
|
}
|
|
16
16
|
<div class="grow pt-1" [ngClass]="shouldShowThumbnail ? 'sm:w-0' : ''">
|
|
17
|
-
<div class="flex flex-col gap-2 h-full">
|
|
17
|
+
<div class="flex flex-col gap-2" [class.h-full]="size !== 'M'">
|
|
18
18
|
<h4
|
|
19
19
|
class="record-card__title"
|
|
20
20
|
data-cy="recordTitle"
|
|
@@ -32,63 +32,70 @@
|
|
|
32
32
|
[title]="abstract"
|
|
33
33
|
></gn-ui-markdown-parser>
|
|
34
34
|
</div>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
@if (size !== 'M') {
|
|
36
|
+
<ng-container [ngTemplateOutlet]="footerTpl"></ng-container>
|
|
37
|
+
}
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
@if (size === 'M') {
|
|
41
|
+
<ng-container [ngTemplateOutlet]="footerTpl"></ng-container>
|
|
42
|
+
}
|
|
43
|
+
</a>
|
|
44
|
+
|
|
45
|
+
<ng-template #footerTpl>
|
|
46
|
+
<div class="record-card__footer">
|
|
47
|
+
@if (record.ownerOrganization?.name) {
|
|
48
|
+
<div
|
|
49
|
+
data-cy="recordOrg"
|
|
50
|
+
class="grow flex flex-row gap-1 items-center text-primary-lighter"
|
|
51
|
+
[ngClass]="displayContactIconOnly ? 'justify-center' : ''"
|
|
52
|
+
>
|
|
53
|
+
<ng-icon
|
|
54
|
+
name="iconoirBank"
|
|
55
|
+
class="text-primary -translate-y-[0.5px] shrink-0"
|
|
56
|
+
[title]="record.ownerOrganization.name"
|
|
57
|
+
></ng-icon>
|
|
58
|
+
@if (!displayContactIconOnly) {
|
|
59
|
+
<span
|
|
60
|
+
data-cy="recordOrgName"
|
|
61
|
+
class="line-clamp-1"
|
|
62
|
+
[title]="record.ownerOrganization.name"
|
|
63
|
+
>{{ record.ownerOrganization.name }}</span
|
|
41
64
|
>
|
|
42
|
-
<ng-icon
|
|
43
|
-
name="iconoirBank"
|
|
44
|
-
class="text-primary -translate-y-[0.5px] shrink-0"
|
|
45
|
-
[title]="record.ownerOrganization.name"
|
|
46
|
-
></ng-icon>
|
|
47
|
-
@if (!displayContactIconOnly) {
|
|
48
|
-
<span
|
|
49
|
-
data-cy="recordOrgName"
|
|
50
|
-
class="line-clamp-1"
|
|
51
|
-
[title]="record.ownerOrganization.name"
|
|
52
|
-
>{{ record.ownerOrganization.name }}</span
|
|
53
|
-
>
|
|
54
|
-
}
|
|
55
|
-
</div>
|
|
56
65
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
66
|
+
</div>
|
|
67
|
+
}
|
|
68
|
+
<div class="record-card__footer__other">
|
|
69
|
+
<div class="xs:border-r last:border-r-0 flex grow gap-4 px-4 last:pr-0">
|
|
70
|
+
<gn-ui-kind-badge
|
|
71
|
+
[extraClass]="'text-[1.2em]'"
|
|
72
|
+
[styling]="'gray'"
|
|
73
|
+
[kind]="record?.kind"
|
|
74
|
+
[contentTemplate]="customTemplate"
|
|
75
|
+
class="pt-1"
|
|
76
|
+
>
|
|
77
|
+
<ng-template #customTemplate></ng-template
|
|
78
|
+
></gn-ui-kind-badge>
|
|
79
|
+
@if (metadataQualityDisplay) {
|
|
80
|
+
<gn-ui-metadata-quality
|
|
81
|
+
class="flex items-center min-w-[113px]"
|
|
82
|
+
[smaller]="true"
|
|
83
|
+
[metadata]="record"
|
|
84
|
+
[metadataQualityDisplay]="metadataQualityDisplay"
|
|
85
|
+
[popoverDisplay]="true"
|
|
86
|
+
></gn-ui-metadata-quality>
|
|
87
|
+
}
|
|
88
|
+
</div>
|
|
89
|
+
<div
|
|
90
|
+
class="flex justify-center"
|
|
91
|
+
data-cy="recordFav"
|
|
92
|
+
[ngClass]="displayContactIconOnly ? 'px-1' : 'px-4'"
|
|
93
|
+
>
|
|
94
|
+
<ng-container
|
|
95
|
+
[ngTemplateOutlet]="favoriteTemplate"
|
|
96
|
+
[ngTemplateOutletContext]="{ $implicit: record }"
|
|
97
|
+
></ng-container>
|
|
91
98
|
</div>
|
|
92
99
|
</div>
|
|
93
100
|
</div>
|
|
94
|
-
</
|
|
101
|
+
</ng-template>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
&.size-M {
|
|
9
|
-
@apply max-w-[940px]
|
|
9
|
+
@apply max-w-[940px] gap-4 flex-wrap;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
&.size-S {
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
@apply border rounded-lg overflow-hidden shrink-0;
|
|
29
29
|
|
|
30
30
|
.size-L & {
|
|
31
|
-
@apply w-
|
|
31
|
+
@apply w-[184px] h-[184px];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.size-M & {
|
|
35
|
-
@apply w-
|
|
35
|
+
@apply w-[138px] h-[138px];
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
.size-M & {
|
|
47
|
-
@apply line-clamp-2
|
|
47
|
+
@apply line-clamp-2;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
.size-S & {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
.size-M & {
|
|
67
|
-
@apply line-clamp-3
|
|
67
|
+
@apply line-clamp-3;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
.size-S & {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
></gn-ui-thumbnail>
|
|
31
31
|
}
|
|
32
32
|
</div>
|
|
33
|
-
<div class="flex flex-col overflow-hidden items-start">
|
|
33
|
+
<div class="flex flex-col overflow-hidden items-start grow">
|
|
34
34
|
@if (hasOrganization) {
|
|
35
35
|
<span
|
|
36
36
|
class="font-bold transition duration-200 text-primary truncate max-w-full"
|
|
@@ -51,32 +51,35 @@
|
|
|
51
51
|
>
|
|
52
52
|
</p>
|
|
53
53
|
</div>
|
|
54
|
-
|
|
55
|
-
<div class="pt-5 pb-5 px-10 relative">
|
|
56
|
-
<div class="absolute top-[0.85em] right-[0.85em]">
|
|
54
|
+
<div class="ml-3 shrink-0">
|
|
57
55
|
<ng-container
|
|
58
56
|
[ngTemplateOutlet]="favoriteTemplate"
|
|
59
57
|
[ngTemplateOutletContext]="{ $implicit: record }"
|
|
60
58
|
></ng-container>
|
|
61
59
|
</div>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
</div>
|
|
61
|
+
<div class="pt-5 pb-5 px-10 relative">
|
|
62
|
+
<div class="flex flex-col sm:flex-row gap-4 mb-3">
|
|
63
|
+
@if (record.overviews?.[0]) {
|
|
64
|
+
<gn-ui-thumbnail
|
|
65
|
+
class="block shrink-0 w-[120px] h-[120px] border border-gray-100 rounded-lg overflow-hidden"
|
|
66
|
+
[thumbnailUrl]="record.overviews?.[0]?.url.toString()"
|
|
67
|
+
[fit]="'cover'"
|
|
68
|
+
></gn-ui-thumbnail>
|
|
69
|
+
}
|
|
70
|
+
<h1
|
|
71
|
+
class="font-title text-black text-[21px] font-medium"
|
|
72
|
+
data-cy="recordTitle"
|
|
73
|
+
>
|
|
74
|
+
{{ record.title }}
|
|
75
|
+
</h1>
|
|
76
|
+
</div>
|
|
68
77
|
<p class="line-clamp-3">
|
|
69
78
|
<gn-ui-markdown-parser
|
|
70
79
|
[textContent]="abstract"
|
|
71
80
|
[whitoutStyles]="true"
|
|
72
81
|
/>
|
|
73
82
|
</p>
|
|
74
|
-
@if (record.overviews?.[0]) {
|
|
75
|
-
<gn-ui-thumbnail
|
|
76
|
-
class="block mt-3 w-full h-[136px] border border-gray-100 rounded-lg overflow-hidden"
|
|
77
|
-
[thumbnailUrl]="record.overviews?.[0]?.url.toString()"
|
|
78
|
-
></gn-ui-thumbnail>
|
|
79
|
-
}
|
|
80
83
|
@if (isDownloadable || isViewable) {
|
|
81
84
|
<div class="flex flex-row mt-3">
|
|
82
85
|
@if (isDownloadable) {
|