@ndla/ui 30.1.0 → 30.2.0
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/es/MyNdla/Resource/Folder.js +118 -57
- package/es/Resource/BlockResource.js +45 -30
- package/es/Resource/ListResource.js +46 -41
- package/es/Resource/resourceComponents.js +29 -34
- package/lib/MyNdla/Resource/Folder.d.ts +1 -1
- package/lib/MyNdla/Resource/Folder.js +115 -57
- package/lib/Resource/BlockResource.d.ts +2 -2
- package/lib/Resource/BlockResource.js +44 -30
- package/lib/Resource/ListResource.js +40 -36
- package/lib/Resource/resourceComponents.js +28 -31
- package/package.json +6 -6
- package/src/MyNdla/Resource/Folder.tsx +118 -84
- package/src/Resource/BlockResource.tsx +34 -28
- package/src/Resource/ListResource.tsx +76 -74
- package/src/Resource/resourceComponents.tsx +15 -20
|
@@ -16,39 +16,72 @@ import { useTranslation } from 'react-i18next';
|
|
|
16
16
|
import SafeLink from '@ndla/safelink';
|
|
17
17
|
import { MenuButton, MenuItemProps } from '@ndla/button';
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
type LayoutType = 'list' | 'listLarger' | 'block';
|
|
20
|
+
interface LayoutProps {
|
|
21
|
+
type: LayoutType;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
const
|
|
24
|
+
const FolderWrapper = styled.div<LayoutProps>`
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
padding: ${spacing.nsmall};
|
|
29
|
+
gap: ${spacing.small};
|
|
30
|
+
|
|
31
|
+
${mq.range({ until: breakpoints.mobileWide })} {
|
|
32
|
+
${({ type }) =>
|
|
33
|
+
type !== 'list' &&
|
|
34
|
+
css`
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
align-items: unset;
|
|
37
|
+
`}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
${({ type }) =>
|
|
41
|
+
type === 'block' &&
|
|
42
|
+
css`
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
align-items: unset;
|
|
45
|
+
`}
|
|
46
|
+
|
|
47
|
+
border: 1px solid ${colors.brand.neutral7};
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
border-radius: 2px;
|
|
50
|
+
box-shadow: none;
|
|
51
|
+
text-decoration: none;
|
|
52
|
+
&:hover {
|
|
53
|
+
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
54
|
+
transition-duration: 0.2s;
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const TitleWrapper = styled.div`
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: row;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: ${spacing.xsmall};
|
|
63
|
+
justify-content: space-between;
|
|
64
|
+
`;
|
|
65
|
+
|
|
66
|
+
const IconWrapper = styled.div`
|
|
24
67
|
display: flex;
|
|
25
68
|
border-radius: 100%;
|
|
26
|
-
|
|
27
|
-
background-color: ${colors.brand.greyLighter};
|
|
69
|
+
color: ${colors.brand.primary};
|
|
28
70
|
svg {
|
|
29
|
-
width:
|
|
30
|
-
height:
|
|
71
|
+
width: 20px;
|
|
72
|
+
height: 20px;
|
|
31
73
|
}
|
|
32
|
-
${(p) =>
|
|
33
|
-
p.type === 'block' &&
|
|
34
|
-
css`
|
|
35
|
-
background-color: transparent;
|
|
36
|
-
${FolderWrapper}:hover & {
|
|
37
|
-
background-color: ${colors.brand.light};
|
|
38
|
-
transition-duration 0.5s;
|
|
39
|
-
}
|
|
40
|
-
`};
|
|
41
74
|
`;
|
|
42
75
|
|
|
43
|
-
const
|
|
76
|
+
const StyledLink = styled(SafeLink)`
|
|
44
77
|
box-shadow: none;
|
|
45
|
-
color: ${colors.
|
|
78
|
+
color: ${colors.brand.primary};
|
|
46
79
|
flex: 1;
|
|
47
80
|
`;
|
|
48
81
|
|
|
49
82
|
const FolderTitle = styled.h2`
|
|
50
|
-
${fonts.sizes(
|
|
51
|
-
font-weight: ${fonts.weight.
|
|
83
|
+
${fonts.sizes('16px', '20px')};
|
|
84
|
+
font-weight: ${fonts.weight.semibold};
|
|
52
85
|
margin: 0;
|
|
53
86
|
flex: 1;
|
|
54
87
|
|
|
@@ -59,41 +92,50 @@ const FolderTitle = styled.h2`
|
|
|
59
92
|
-webkit-line-clamp: 1;
|
|
60
93
|
line-clamp: 1;
|
|
61
94
|
-webkit-box-orient: vertical;
|
|
95
|
+
|
|
96
|
+
${FolderWrapper}:hover & {
|
|
97
|
+
color: ${colors.brand.primary};
|
|
98
|
+
text-decoration: underline;
|
|
99
|
+
}
|
|
62
100
|
`;
|
|
63
101
|
|
|
64
|
-
const
|
|
102
|
+
const MenuWrapper = styled.div`
|
|
103
|
+
overflow: hidden;
|
|
65
104
|
display: flex;
|
|
105
|
+
flex-direction: row;
|
|
66
106
|
align-items: center;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
107
|
+
gap: ${spacing.xsmall};
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
margin: -${spacing.nsmall};
|
|
110
|
+
margin-left: 0;
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
const CountContainer = styled.div`
|
|
114
|
+
overflow: hidden;
|
|
115
|
+
display: flex;
|
|
116
|
+
flex-direction: row;
|
|
76
117
|
gap: ${spacing.small};
|
|
77
|
-
&:hover {
|
|
78
|
-
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
79
|
-
transition-duration: 0.2s;
|
|
80
|
-
${FolderTitle} {
|
|
81
|
-
color: ${colors.brand.primary};
|
|
82
|
-
text-decoration: underline;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
118
|
`;
|
|
86
119
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
120
|
+
const IconCountWrapper = styled.div<LayoutProps>`
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: ${spacing.xxsmall};
|
|
124
|
+
color: ${colors.brand.grey};
|
|
125
|
+
white-space: nowrap;
|
|
126
|
+
svg {
|
|
127
|
+
width: 13px;
|
|
128
|
+
height: 13px;
|
|
129
|
+
}
|
|
130
|
+
${fonts.sizes(16)};
|
|
131
|
+
${mq.range({ until: breakpoints.mobileWide })} {
|
|
132
|
+
${({ type }) =>
|
|
133
|
+
type === 'list' &&
|
|
134
|
+
css`
|
|
135
|
+
display: none;
|
|
136
|
+
`}
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
97
139
|
|
|
98
140
|
interface IconCountProps {
|
|
99
141
|
type: 'resource' | 'folder';
|
|
@@ -101,30 +143,7 @@ interface IconCountProps {
|
|
|
101
143
|
layoutType: LayoutType;
|
|
102
144
|
}
|
|
103
145
|
|
|
104
|
-
|
|
105
|
-
type: LayoutType;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const IconCountWrapper = styled.div<IconCountWrapperProps>`
|
|
109
|
-
display: flex;
|
|
110
|
-
align-items: center;
|
|
111
|
-
gap: 2px;
|
|
112
|
-
${fonts.sizes(16)};
|
|
113
|
-
${(p) =>
|
|
114
|
-
p.type === 'block' &&
|
|
115
|
-
css`
|
|
116
|
-
opacity: 0;
|
|
117
|
-
${FolderWrapper}:hover & {
|
|
118
|
-
opacity: 1;
|
|
119
|
-
}
|
|
120
|
-
`};
|
|
121
|
-
|
|
122
|
-
${mq.range({ until: breakpoints.tabletWide })} {
|
|
123
|
-
display: none;
|
|
124
|
-
}
|
|
125
|
-
`;
|
|
126
|
-
|
|
127
|
-
const IconCount = ({ type, count, layoutType }: IconCountProps) => {
|
|
146
|
+
const Count = ({ type, count, layoutType }: IconCountProps) => {
|
|
128
147
|
const Icon = type === 'resource' ? FileDocumentOutline : FolderOutlined;
|
|
129
148
|
const { t } = useTranslation();
|
|
130
149
|
if (!count) return null;
|
|
@@ -132,12 +151,21 @@ const IconCount = ({ type, count, layoutType }: IconCountProps) => {
|
|
|
132
151
|
return (
|
|
133
152
|
<IconCountWrapper type={layoutType}>
|
|
134
153
|
<Icon aria-label={t(`myNdla.${type}s`)} />
|
|
135
|
-
<span>{
|
|
154
|
+
<span>{t(`myNdla.${type}s`, { count })}</span>
|
|
136
155
|
</IconCountWrapper>
|
|
137
156
|
);
|
|
138
157
|
};
|
|
139
158
|
|
|
140
|
-
|
|
159
|
+
interface Props {
|
|
160
|
+
id: string;
|
|
161
|
+
title: string;
|
|
162
|
+
subFolders?: number;
|
|
163
|
+
subResources?: number;
|
|
164
|
+
description?: string;
|
|
165
|
+
link: string;
|
|
166
|
+
type: LayoutType;
|
|
167
|
+
menuItems?: MenuItemProps[];
|
|
168
|
+
}
|
|
141
169
|
|
|
142
170
|
const Folder = ({ id, link, title, subFolders, subResources, type = 'list', menuItems }: Props) => {
|
|
143
171
|
const { t } = useTranslation();
|
|
@@ -148,16 +176,22 @@ const Folder = ({ id, link, title, subFolders, subResources, type = 'list', menu
|
|
|
148
176
|
};
|
|
149
177
|
|
|
150
178
|
return (
|
|
151
|
-
<FolderWrapper onClick={onClick} id={id}>
|
|
152
|
-
<
|
|
153
|
-
<
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
179
|
+
<FolderWrapper type={type} onClick={onClick} id={id}>
|
|
180
|
+
<TitleWrapper>
|
|
181
|
+
<IconWrapper>
|
|
182
|
+
<FolderOutlined aria-label={t('myNdla.folder.folder')} />
|
|
183
|
+
</IconWrapper>
|
|
184
|
+
<StyledLink to={link} ref={linkRef}>
|
|
185
|
+
<FolderTitle>{title}</FolderTitle>
|
|
186
|
+
</StyledLink>
|
|
187
|
+
</TitleWrapper>
|
|
188
|
+
<MenuWrapper>
|
|
189
|
+
<CountContainer>
|
|
190
|
+
<Count layoutType={type} type={'folder'} count={subFolders} />
|
|
191
|
+
<Count layoutType={type} type={'resource'} count={subResources} />
|
|
192
|
+
</CountContainer>
|
|
193
|
+
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
194
|
+
</MenuWrapper>
|
|
161
195
|
</FolderWrapper>
|
|
162
196
|
);
|
|
163
197
|
};
|
|
@@ -25,27 +25,12 @@ import {
|
|
|
25
25
|
import ContentLoader from '../ContentLoader';
|
|
26
26
|
import { contentTypeMapping } from '../model/ContentType';
|
|
27
27
|
|
|
28
|
-
interface BlockResourceProps {
|
|
29
|
-
id: string;
|
|
30
|
-
link: string;
|
|
31
|
-
tagLinkPrefix?: string;
|
|
32
|
-
title: string;
|
|
33
|
-
resourceImage: ResourceImageProps;
|
|
34
|
-
tags?: string[];
|
|
35
|
-
description?: string;
|
|
36
|
-
headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
37
|
-
menuItems?: MenuItemProps[];
|
|
38
|
-
isLoading?: boolean;
|
|
39
|
-
targetBlank?: boolean;
|
|
40
|
-
resourceTypes?: { id: string; name: string }[];
|
|
41
|
-
}
|
|
42
|
-
|
|
43
28
|
const BlockElementWrapper = styled.div`
|
|
44
29
|
display: flex;
|
|
45
30
|
text-decoration: none;
|
|
46
31
|
box-shadow: none;
|
|
47
32
|
flex-direction: column;
|
|
48
|
-
max-width:
|
|
33
|
+
max-width: 450px;
|
|
49
34
|
max-height: 240px;
|
|
50
35
|
border: 1px solid ${colors.brand.light};
|
|
51
36
|
border-radius: 2px;
|
|
@@ -83,14 +68,18 @@ const BlockDescription = styled.p`
|
|
|
83
68
|
|
|
84
69
|
const RightRow = styled(Row)`
|
|
85
70
|
justify-content: flex-end;
|
|
86
|
-
margin
|
|
71
|
+
margin: 0 -${spacing.small} -${spacing.small} 0;
|
|
87
72
|
`;
|
|
88
73
|
|
|
89
74
|
const BlockInfoWrapper = styled.div`
|
|
90
75
|
display: flex;
|
|
91
76
|
flex-direction: column;
|
|
92
77
|
padding: ${spacing.small};
|
|
93
|
-
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const ContentWrapper = styled.div`
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
94
83
|
`;
|
|
95
84
|
|
|
96
85
|
const ImageWrapper = styled.div`
|
|
@@ -99,11 +88,11 @@ const ImageWrapper = styled.div`
|
|
|
99
88
|
justify-content: center;
|
|
100
89
|
overflow: hidden;
|
|
101
90
|
align-items: center;
|
|
102
|
-
aspect-ratio: 3/4;
|
|
103
91
|
img {
|
|
104
92
|
min-width: 100%;
|
|
105
93
|
}
|
|
106
94
|
`;
|
|
95
|
+
|
|
107
96
|
interface BlockImageProps {
|
|
108
97
|
image: ResourceImageProps;
|
|
109
98
|
loading?: boolean;
|
|
@@ -142,6 +131,21 @@ const ResourceTypeAndTitleLoader = ({ children, loading }: LoaderProps) => {
|
|
|
142
131
|
return <>{children}</>;
|
|
143
132
|
};
|
|
144
133
|
|
|
134
|
+
interface Props {
|
|
135
|
+
id: string;
|
|
136
|
+
link: string;
|
|
137
|
+
tagLinkPrefix?: string;
|
|
138
|
+
title: string;
|
|
139
|
+
resourceImage: ResourceImageProps;
|
|
140
|
+
tags?: string[];
|
|
141
|
+
description?: string;
|
|
142
|
+
headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
143
|
+
menuItems?: MenuItemProps[];
|
|
144
|
+
isLoading?: boolean;
|
|
145
|
+
targetBlank?: boolean;
|
|
146
|
+
resourceTypes?: { id: string; name: string }[];
|
|
147
|
+
}
|
|
148
|
+
|
|
145
149
|
const BlockResource = ({
|
|
146
150
|
id,
|
|
147
151
|
link,
|
|
@@ -155,7 +159,7 @@ const BlockResource = ({
|
|
|
155
159
|
headingLevel = 'h2',
|
|
156
160
|
targetBlank,
|
|
157
161
|
resourceTypes,
|
|
158
|
-
}:
|
|
162
|
+
}: Props) => {
|
|
159
163
|
const linkRef = useRef<HTMLAnchorElement>(null);
|
|
160
164
|
const firstResourceType = resourceTypes?.[0].id ?? '';
|
|
161
165
|
const Title = ResourceTitle.withComponent(headingLevel);
|
|
@@ -176,15 +180,17 @@ const BlockResource = ({
|
|
|
176
180
|
/>
|
|
177
181
|
</ImageWrapper>
|
|
178
182
|
<BlockInfoWrapper>
|
|
179
|
-
<
|
|
180
|
-
<
|
|
181
|
-
<
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
<ContentWrapper>
|
|
184
|
+
<ResourceTypeAndTitleLoader loading={isLoading}>
|
|
185
|
+
<ResourceTitleLink title={title} target={targetBlank ? '_blank' : undefined} to={link} ref={linkRef}>
|
|
186
|
+
<Title>{title}</Title>
|
|
187
|
+
</ResourceTitleLink>
|
|
188
|
+
</ResourceTypeAndTitleLoader>
|
|
189
|
+
<ResourceTypeList resourceTypes={resourceTypes} />
|
|
190
|
+
<BlockDescription>{description}</BlockDescription>
|
|
191
|
+
</ContentWrapper>
|
|
186
192
|
<RightRow>
|
|
187
|
-
{tags && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
193
|
+
{tags && tags.length > 0 && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
188
194
|
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
189
195
|
</RightRow>
|
|
190
196
|
</BlockInfoWrapper>
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
CompressedTagList,
|
|
16
16
|
ResourceImageProps,
|
|
17
17
|
ResourceTitle,
|
|
18
|
-
ResourceTitleLink,
|
|
18
|
+
ResourceTitleLink as StyledLink,
|
|
19
19
|
ResourceTypeList,
|
|
20
20
|
StyledContentIconWrapper,
|
|
21
21
|
LoaderProps,
|
|
@@ -24,30 +24,13 @@ import ContentLoader from '../ContentLoader';
|
|
|
24
24
|
import ContentTypeBadge from '../ContentTypeBadge';
|
|
25
25
|
import { contentTypeMapping } from '../model/ContentType';
|
|
26
26
|
|
|
27
|
-
const
|
|
28
|
-
grid-area: description;
|
|
29
|
-
line-clamp: 2;
|
|
30
|
-
line-height: 1em;
|
|
31
|
-
height: 3.1em;
|
|
32
|
-
margin: 0;
|
|
33
|
-
overflow: hidden;
|
|
34
|
-
${fonts.sizes(16)};
|
|
35
|
-
text-overflow: ellipsis;
|
|
36
|
-
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
37
|
-
display: -webkit-box;
|
|
38
|
-
-webkit-line-clamp: 2;
|
|
39
|
-
line-clamp: 2;
|
|
40
|
-
-webkit-box-orient: vertical;
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
const ResourceWrapper = styled.div`
|
|
27
|
+
const ListResourceWrapper = styled.div`
|
|
44
28
|
flex: 1;
|
|
45
29
|
display: grid;
|
|
46
30
|
grid-template-columns: auto 1fr auto;
|
|
47
31
|
grid-template-areas:
|
|
48
32
|
'image topicAndTitle tags'
|
|
49
33
|
'image description description';
|
|
50
|
-
|
|
51
34
|
${mq.range({ until: breakpoints.mobileWide })} {
|
|
52
35
|
grid-template-columns: auto 1fr;
|
|
53
36
|
grid-template-areas:
|
|
@@ -56,46 +39,31 @@ const ResourceWrapper = styled.div`
|
|
|
56
39
|
'tags tags';
|
|
57
40
|
}
|
|
58
41
|
|
|
59
|
-
cursor: pointer;
|
|
60
42
|
padding: ${spacing.small};
|
|
43
|
+
column-gap: ${spacing.small};
|
|
44
|
+
cursor: pointer;
|
|
61
45
|
border: 1px solid ${colors.brand.neutral7};
|
|
62
46
|
border-radius: 2px;
|
|
63
|
-
gap: 0 ${spacing.small};
|
|
64
47
|
|
|
65
48
|
&:hover {
|
|
66
49
|
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
67
50
|
transition-duration: 0.2s;
|
|
68
|
-
${() =>
|
|
51
|
+
${() => StyledLink} {
|
|
69
52
|
color: ${colors.brand.primary};
|
|
70
53
|
text-decoration: underline;
|
|
71
54
|
}
|
|
72
55
|
}
|
|
73
56
|
`;
|
|
74
57
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
grid-area: tags;
|
|
79
|
-
display: flex;
|
|
80
|
-
align-items: center;
|
|
81
|
-
width: 100%;
|
|
82
|
-
overflow: hidden;
|
|
83
|
-
align-self: flex-start;
|
|
84
|
-
justify-self: flex-end;
|
|
85
|
-
justify-content: flex-end;
|
|
86
|
-
|
|
87
|
-
${mq.range({ from: breakpoints.mobileWide })} {
|
|
88
|
-
margin-top: -${spacing.xsmall};
|
|
89
|
-
margin-right: -${spacing.xxsmall};
|
|
90
|
-
}
|
|
91
|
-
`;
|
|
58
|
+
interface StyledImageProps {
|
|
59
|
+
imageSize: 'normal' | 'compact';
|
|
60
|
+
}
|
|
92
61
|
|
|
93
|
-
const
|
|
62
|
+
const ImageWrapper = styled.div<StyledImageProps>`
|
|
94
63
|
grid-area: image;
|
|
95
64
|
width: ${(p) => (p.imageSize === 'normal' ? '136px' : '56px')};
|
|
96
65
|
${mq.range({ until: breakpoints.mobileWide })} {
|
|
97
|
-
width:
|
|
98
|
-
height: 40px;
|
|
66
|
+
width: 56px;
|
|
99
67
|
}
|
|
100
68
|
overflow: hidden;
|
|
101
69
|
border-radius: 2px;
|
|
@@ -110,29 +78,48 @@ const StyledImage = styled(Image)`
|
|
|
110
78
|
aspect-ratio: 4/3;
|
|
111
79
|
`;
|
|
112
80
|
|
|
81
|
+
const StyledResourceDescription = styled.p`
|
|
82
|
+
grid-area: description;
|
|
83
|
+
line-clamp: 2;
|
|
84
|
+
line-height: 1em;
|
|
85
|
+
height: 3.1em;
|
|
86
|
+
margin: 0;
|
|
87
|
+
margin-top: ${spacing.xxsmall};
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
${fonts.sizes(16)};
|
|
90
|
+
text-overflow: ellipsis;
|
|
91
|
+
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
92
|
+
display: -webkit-box;
|
|
93
|
+
-webkit-line-clamp: 2;
|
|
94
|
+
line-clamp: 2;
|
|
95
|
+
-webkit-box-orient: vertical;
|
|
96
|
+
`;
|
|
97
|
+
|
|
98
|
+
const TagsandActionMenu = styled.div`
|
|
99
|
+
box-sizing: content-box;
|
|
100
|
+
grid-area: tags;
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
width: 100%;
|
|
104
|
+
overflow: hidden;
|
|
105
|
+
align-self: flex-start;
|
|
106
|
+
justify-self: flex-end;
|
|
107
|
+
justify-content: flex-end;
|
|
108
|
+
margin: -${spacing.small} -${spacing.small} 0 0;
|
|
109
|
+
${mq.range({ until: breakpoints.mobileWide })} {
|
|
110
|
+
margin: 0 -${spacing.small} -${spacing.small} 0;
|
|
111
|
+
}
|
|
112
|
+
`;
|
|
113
|
+
|
|
113
114
|
const TopicAndTitleWrapper = styled.div`
|
|
114
115
|
grid-area: topicAndTitle;
|
|
115
|
-
margin-top: 2px;
|
|
116
116
|
`;
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
id: string;
|
|
124
|
-
link: string;
|
|
125
|
-
tagLinkPrefix?: string;
|
|
126
|
-
title: string;
|
|
127
|
-
resourceImage: ResourceImageProps;
|
|
128
|
-
headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
129
|
-
resourceTypes: { id: string; name: string }[];
|
|
130
|
-
tags?: string[];
|
|
131
|
-
description?: string;
|
|
132
|
-
menuItems?: MenuItemProps[];
|
|
133
|
-
isLoading?: boolean;
|
|
134
|
-
targetBlank?: boolean;
|
|
135
|
-
}
|
|
118
|
+
const StyledMenuButton = styled(MenuButton)`
|
|
119
|
+
:only-child {
|
|
120
|
+
margin-left: ${spacing.small};
|
|
121
|
+
}
|
|
122
|
+
`;
|
|
136
123
|
|
|
137
124
|
interface ListResourceImageProps {
|
|
138
125
|
resourceImage: ResourceImageProps;
|
|
@@ -170,7 +157,7 @@ const ListResourceImage = ({ resourceImage, loading, type, contentType }: ListRe
|
|
|
170
157
|
);
|
|
171
158
|
};
|
|
172
159
|
|
|
173
|
-
const
|
|
160
|
+
const TypeAndTitleLoader = ({ loading, children }: LoaderProps) => {
|
|
174
161
|
if (loading) {
|
|
175
162
|
return (
|
|
176
163
|
<ContentLoader height={'40px'} width={'100%'} viewBox={null} preserveAspectRatio="none">
|
|
@@ -188,7 +175,7 @@ interface ResourceDescriptionProps {
|
|
|
188
175
|
loading?: boolean;
|
|
189
176
|
}
|
|
190
177
|
|
|
191
|
-
const
|
|
178
|
+
const Description = ({ description, loading }: ResourceDescriptionProps) => {
|
|
192
179
|
if (loading) {
|
|
193
180
|
return (
|
|
194
181
|
<ContentLoader height={'20px'} width={'100%'} viewBox={null} preserveAspectRatio="none">
|
|
@@ -199,6 +186,21 @@ const ResourceDescription = ({ description, loading }: ResourceDescriptionProps)
|
|
|
199
186
|
return <StyledResourceDescription>{description}</StyledResourceDescription>;
|
|
200
187
|
};
|
|
201
188
|
|
|
189
|
+
export interface ListResourceProps {
|
|
190
|
+
id: string;
|
|
191
|
+
link: string;
|
|
192
|
+
tagLinkPrefix?: string;
|
|
193
|
+
title: string;
|
|
194
|
+
resourceImage: ResourceImageProps;
|
|
195
|
+
headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
|
|
196
|
+
resourceTypes: { id: string; name: string }[];
|
|
197
|
+
tags?: string[];
|
|
198
|
+
description?: string;
|
|
199
|
+
menuItems?: MenuItemProps[];
|
|
200
|
+
isLoading?: boolean;
|
|
201
|
+
targetBlank?: boolean;
|
|
202
|
+
}
|
|
203
|
+
|
|
202
204
|
const ListResource = ({
|
|
203
205
|
id,
|
|
204
206
|
link,
|
|
@@ -225,29 +227,29 @@ const ListResource = ({
|
|
|
225
227
|
};
|
|
226
228
|
|
|
227
229
|
return (
|
|
228
|
-
<
|
|
229
|
-
<
|
|
230
|
+
<ListResourceWrapper onClick={handleClick} id={id}>
|
|
231
|
+
<ImageWrapper imageSize={imageType}>
|
|
230
232
|
<ListResourceImage
|
|
231
233
|
resourceImage={resourceImage}
|
|
232
234
|
loading={isLoading}
|
|
233
235
|
type={imageType}
|
|
234
236
|
contentType={contentTypeMapping[firstContentType] ?? contentTypeMapping['default']}
|
|
235
237
|
/>
|
|
236
|
-
</
|
|
238
|
+
</ImageWrapper>
|
|
237
239
|
<TopicAndTitleWrapper>
|
|
238
|
-
<
|
|
239
|
-
<
|
|
240
|
+
<TypeAndTitleLoader loading={isLoading}>
|
|
241
|
+
<StyledLink to={link} target={targetBlank ? '_blank' : undefined} ref={linkRef}>
|
|
240
242
|
<Title>{title}</Title>
|
|
241
|
-
</
|
|
243
|
+
</StyledLink>
|
|
242
244
|
<ResourceTypeList resourceTypes={resourceTypes} />
|
|
243
|
-
</
|
|
245
|
+
</TypeAndTitleLoader>
|
|
244
246
|
</TopicAndTitleWrapper>
|
|
245
|
-
{showDescription && <
|
|
247
|
+
{showDescription && <Description description={description} loading={isLoading} />}
|
|
246
248
|
<TagsandActionMenu>
|
|
247
|
-
{tags && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
248
|
-
{menuItems && menuItems.length > 0 && <
|
|
249
|
+
{tags && tags.length > 0 && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
250
|
+
{menuItems && menuItems.length > 0 && <StyledMenuButton alignRight size="small" menuItems={menuItems} />}
|
|
249
251
|
</TagsandActionMenu>
|
|
250
|
-
</
|
|
252
|
+
</ListResourceWrapper>
|
|
251
253
|
);
|
|
252
254
|
};
|
|
253
255
|
|