@ndla/ui 42.1.0 → 42.1.2
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/AudioPlayer/AudioPlayer.js +27 -33
- package/es/CampaignBlock/CampaignBlock.js +32 -18
- package/es/LicenseByline/EmbedByline.js +5 -5
- package/es/MyNdla/Resource/Folder.js +27 -66
- package/es/MyNdla/Resource/FolderInput.js +8 -4
- package/es/Resource/BlockResource.js +13 -22
- package/es/Resource/ListResource.js +12 -14
- package/es/Resource/resourceComponents.js +59 -29
- package/es/TreeStructure/ComboboxButton.js +17 -20
- package/es/TreeStructure/FolderItem.js +48 -70
- package/es/TreeStructure/FolderItems.js +25 -19
- package/es/TreeStructure/TreeStructure.js +19 -26
- package/es/index.js +2 -1
- package/lib/AudioPlayer/AudioPlayer.js +27 -33
- package/lib/CampaignBlock/CampaignBlock.js +32 -18
- package/lib/LicenseByline/EmbedByline.js +5 -5
- package/lib/MyNdla/Resource/Folder.js +27 -66
- package/lib/MyNdla/Resource/FolderInput.js +8 -4
- package/lib/Resource/BlockResource.d.ts +1 -1
- package/lib/Resource/BlockResource.js +12 -21
- package/lib/Resource/ListResource.js +11 -13
- package/lib/Resource/resourceComponents.d.ts +5 -10
- package/lib/Resource/resourceComponents.js +63 -31
- package/lib/TreeStructure/ComboboxButton.js +17 -20
- package/lib/TreeStructure/FolderItem.js +46 -68
- package/lib/TreeStructure/FolderItems.js +31 -26
- package/lib/TreeStructure/TreeStructure.js +19 -26
- package/lib/index.d.ts +1 -0
- package/lib/index.js +7 -0
- package/package.json +14 -14
- package/src/AudioPlayer/AudioPlayer.tsx +24 -34
- package/src/CampaignBlock/CampaignBlock.tsx +10 -10
- package/src/LicenseByline/EmbedByline.tsx +1 -1
- package/src/MyNdla/Resource/Folder.tsx +28 -35
- package/src/MyNdla/Resource/FolderInput.tsx +5 -1
- package/src/Resource/BlockResource.tsx +21 -18
- package/src/Resource/ListResource.tsx +17 -12
- package/src/Resource/resourceComponents.tsx +34 -15
- package/src/TreeStructure/ComboboxButton.tsx +5 -7
- package/src/TreeStructure/FolderItem.tsx +51 -33
- package/src/TreeStructure/FolderItems.tsx +6 -8
- package/src/TreeStructure/TreeStructure.tsx +16 -25
- package/src/index.ts +2 -0
|
@@ -53,22 +53,17 @@ const ImageWrapper = styled.div`
|
|
|
53
53
|
}
|
|
54
54
|
`;
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
hasImage?: boolean;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const TextWrapper = styled.div<TextWrapperProps>`
|
|
56
|
+
const TextWrapper = styled.div`
|
|
61
57
|
padding: ${spacing.small};
|
|
62
58
|
width: 100%;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
&[data-has-image='true'] {
|
|
60
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
61
|
+
padding: ${spacing.small} ${spacing.normal};
|
|
62
|
+
}
|
|
63
|
+
${mq.range({ from: breakpoints.tabletWide })} {
|
|
64
|
+
padding: ${spacing.small} ${spacing.medium};
|
|
65
|
+
}
|
|
68
66
|
}
|
|
69
|
-
${mq.range({ from: breakpoints.tabletWide })} {
|
|
70
|
-
padding: ${spacing.small} ${spacing.medium};
|
|
71
|
-
}`}
|
|
72
67
|
`;
|
|
73
68
|
|
|
74
69
|
const TitleWrapper = styled.div`
|
|
@@ -78,13 +73,12 @@ const TitleWrapper = styled.div`
|
|
|
78
73
|
}
|
|
79
74
|
`;
|
|
80
75
|
|
|
81
|
-
|
|
82
|
-
hasDescription?: boolean;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const Title = styled.h2<TitleProps>`
|
|
76
|
+
const Title = styled.h2`
|
|
86
77
|
${fonts.sizes('22px', '30px')};
|
|
87
|
-
margin:
|
|
78
|
+
margin: 0px;
|
|
79
|
+
&[data-has-desc='true'] {
|
|
80
|
+
margin: 0 0 ${spacing.small};
|
|
81
|
+
}
|
|
88
82
|
`;
|
|
89
83
|
|
|
90
84
|
const Subtitle = styled.h3`
|
|
@@ -99,14 +93,10 @@ const StyledDescription = styled.div`
|
|
|
99
93
|
margin: 0;
|
|
100
94
|
`;
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
${(props) =>
|
|
107
|
-
!props.noMargin &&
|
|
108
|
-
`margin-top: ${spacing.normal};
|
|
109
|
-
`}
|
|
96
|
+
const LinkToTextVersionWrapper = styled.div`
|
|
97
|
+
&[data-margin='true'] {
|
|
98
|
+
margin-top: ${spacing.small};
|
|
99
|
+
}
|
|
110
100
|
${mq.range({ until: breakpoints.tabletWide })} {
|
|
111
101
|
margin: ${spacing.small} 0;
|
|
112
102
|
}
|
|
@@ -201,10 +191,10 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
|
|
|
201
191
|
};
|
|
202
192
|
|
|
203
193
|
type TextVersionComponentProps = {
|
|
204
|
-
|
|
194
|
+
margin?: boolean;
|
|
205
195
|
};
|
|
206
|
-
const TextVersionComponent = ({
|
|
207
|
-
<LinkToTextVersionWrapper
|
|
196
|
+
const TextVersionComponent = ({ margin }: TextVersionComponentProps) => (
|
|
197
|
+
<LinkToTextVersionWrapper data-margin={margin}>
|
|
208
198
|
<ButtonV2 size="normal" shape="pill" onClick={toggleTextVersion} data-audio-text-button-id={staticRenderId}>
|
|
209
199
|
{t('audio.textVersion.heading')}
|
|
210
200
|
</ButtonV2>
|
|
@@ -219,7 +209,7 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
|
|
|
219
209
|
<img src={img.url} alt={img.alt} />
|
|
220
210
|
</ImageWrapper>
|
|
221
211
|
)}
|
|
222
|
-
<TextWrapper
|
|
212
|
+
<TextWrapper data-has-image={!!img}>
|
|
223
213
|
<TitleWrapper>
|
|
224
214
|
<div>
|
|
225
215
|
{subtitle && (
|
|
@@ -227,9 +217,9 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
|
|
|
227
217
|
{subtitle.url ? <SafeLink to={subtitle.url}>{subtitle.title}</SafeLink> : subtitle.title}
|
|
228
218
|
</Subtitle>
|
|
229
219
|
)}
|
|
230
|
-
<Title
|
|
220
|
+
<Title data-has-desc={!!description}>{title}</Title>
|
|
231
221
|
</div>
|
|
232
|
-
{textVersion && !img && <TextVersionComponent
|
|
222
|
+
{textVersion && !img && <TextVersionComponent />}
|
|
233
223
|
</TitleWrapper>
|
|
234
224
|
{description && (
|
|
235
225
|
<StyledDescription>
|
|
@@ -241,7 +231,7 @@ const AudioPlayer = ({ src, title, subtitle, speech, description, img, textVersi
|
|
|
241
231
|
</ButtonV2>
|
|
242
232
|
</StyledDescription>
|
|
243
233
|
)}
|
|
244
|
-
{textVersion && img && <TextVersionComponent />}
|
|
234
|
+
{textVersion && img && <TextVersionComponent margin />}
|
|
245
235
|
</TextWrapper>
|
|
246
236
|
</InfoWrapper>
|
|
247
237
|
<div data-audio-player={1} data-src={src} data-title={title}>
|
|
@@ -18,6 +18,7 @@ interface Image {
|
|
|
18
18
|
src: string;
|
|
19
19
|
alt: string;
|
|
20
20
|
}
|
|
21
|
+
|
|
21
22
|
interface Props {
|
|
22
23
|
title: {
|
|
23
24
|
title: string;
|
|
@@ -62,12 +63,7 @@ const StyledDescription = styled.p`
|
|
|
62
63
|
|
|
63
64
|
const StyledImg = styled.img`
|
|
64
65
|
max-height: 200px;
|
|
65
|
-
|
|
66
|
-
align-self: center;
|
|
67
|
-
}
|
|
68
|
-
${mq.range({ from: breakpoints.tabletWide })} {
|
|
69
|
-
align-self: center;
|
|
70
|
-
}
|
|
66
|
+
align-self: center;
|
|
71
67
|
`;
|
|
72
68
|
|
|
73
69
|
const StyledLink = styled(SafeLink)`
|
|
@@ -84,6 +80,10 @@ const StyledLink = styled(SafeLink)`
|
|
|
84
80
|
}
|
|
85
81
|
`;
|
|
86
82
|
|
|
83
|
+
const TextWrapper = styled.div`
|
|
84
|
+
flex-grow: 1;
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
87
|
const CampaignBlock = ({
|
|
88
88
|
title,
|
|
89
89
|
imageBefore,
|
|
@@ -95,16 +95,16 @@ const CampaignBlock = ({
|
|
|
95
95
|
}: Props) => {
|
|
96
96
|
return (
|
|
97
97
|
<Container className={className}>
|
|
98
|
-
{imageBefore && <StyledImg src={imageBefore.src}
|
|
99
|
-
<
|
|
98
|
+
{imageBefore && <StyledImg src={imageBefore.src} />}
|
|
99
|
+
<TextWrapper>
|
|
100
100
|
<Heading css={headingStyle}>{title.title}</Heading>
|
|
101
101
|
<StyledDescription>{description.text}</StyledDescription>
|
|
102
102
|
<StyledLink to={url.url}>
|
|
103
103
|
{url.text}
|
|
104
104
|
<Forward />
|
|
105
105
|
</StyledLink>
|
|
106
|
-
</
|
|
107
|
-
{imageAfter && <StyledImg src={imageAfter.src}
|
|
106
|
+
</TextWrapper>
|
|
107
|
+
{imageAfter && <StyledImg src={imageAfter.src} />}
|
|
108
108
|
</Container>
|
|
109
109
|
);
|
|
110
110
|
};
|
|
@@ -11,38 +11,30 @@ import React from 'react';
|
|
|
11
11
|
import { FolderOutlined, FolderShared } from '@ndla/icons/contentType';
|
|
12
12
|
import { FileDocumentOutline, Share } from '@ndla/icons/common';
|
|
13
13
|
import { fonts, spacing, colors, mq, breakpoints } from '@ndla/core';
|
|
14
|
-
import { css } from '@emotion/react';
|
|
15
14
|
import { useTranslation } from 'react-i18next';
|
|
16
15
|
import { MenuItemProps } from '@ndla/button';
|
|
17
16
|
import { ResourceTitleLink } from '../../Resource/resourceComponents';
|
|
18
17
|
import FolderMenu from './FolderMenu';
|
|
19
18
|
|
|
20
19
|
export type LayoutType = 'list' | 'listLarger' | 'block';
|
|
21
|
-
interface LayoutProps {
|
|
22
|
-
type: LayoutType;
|
|
23
|
-
}
|
|
24
20
|
|
|
25
|
-
const FolderWrapper = styled.div
|
|
21
|
+
const FolderWrapper = styled.div`
|
|
26
22
|
display: flex;
|
|
27
23
|
position: relative;
|
|
28
24
|
align-items: center;
|
|
29
25
|
justify-content: space-between;
|
|
30
26
|
|
|
31
27
|
${mq.range({ until: breakpoints.mobileWide })} {
|
|
32
|
-
|
|
33
|
-
type !== 'list' &&
|
|
34
|
-
css`
|
|
35
|
-
flex-direction: column;
|
|
36
|
-
align-items: unset;
|
|
37
|
-
`}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
${({ type }) =>
|
|
41
|
-
type === 'block' &&
|
|
42
|
-
css`
|
|
28
|
+
&:not([data-type='list']) {
|
|
43
29
|
flex-direction: column;
|
|
44
30
|
align-items: unset;
|
|
45
|
-
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&[data-type='block'] {
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
align-items: unset;
|
|
37
|
+
}
|
|
46
38
|
|
|
47
39
|
border: 1px solid ${colors.brand.neutral7};
|
|
48
40
|
cursor: pointer;
|
|
@@ -52,17 +44,23 @@ const FolderWrapper = styled.div<LayoutProps>`
|
|
|
52
44
|
&:hover {
|
|
53
45
|
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
54
46
|
transition-duration: 0.2s;
|
|
47
|
+
[data-title] {
|
|
48
|
+
color: ${colors.brand.primary};
|
|
49
|
+
text-decoration: underline;
|
|
50
|
+
}
|
|
55
51
|
}
|
|
56
52
|
`;
|
|
57
53
|
|
|
58
|
-
const TitleWrapper = styled.div
|
|
54
|
+
const TitleWrapper = styled.div`
|
|
59
55
|
display: flex;
|
|
60
56
|
margin: ${spacing.nsmall};
|
|
61
|
-
margin-bottom: ${({ type }) => type === 'block' && 0};
|
|
62
57
|
flex-direction: row;
|
|
63
58
|
align-items: center;
|
|
64
59
|
gap: ${spacing.xsmall};
|
|
65
60
|
justify-content: space-between;
|
|
61
|
+
&[data-type='block'] {
|
|
62
|
+
margin-bottom: 0;
|
|
63
|
+
}
|
|
66
64
|
`;
|
|
67
65
|
|
|
68
66
|
const IconWrapper = styled.div`
|
|
@@ -88,11 +86,6 @@ const FolderTitle = styled.h2`
|
|
|
88
86
|
-webkit-line-clamp: 1;
|
|
89
87
|
line-clamp: 1;
|
|
90
88
|
-webkit-box-orient: vertical;
|
|
91
|
-
|
|
92
|
-
${FolderWrapper}:hover & {
|
|
93
|
-
color: ${colors.brand.primary};
|
|
94
|
-
text-decoration: underline;
|
|
95
|
-
}
|
|
96
89
|
`;
|
|
97
90
|
|
|
98
91
|
const MenuWrapper = styled.div`
|
|
@@ -113,7 +106,7 @@ const CountContainer = styled.div`
|
|
|
113
106
|
margin: 0 ${spacing.small} 0 ${spacing.nsmall};
|
|
114
107
|
`;
|
|
115
108
|
|
|
116
|
-
const IconTextWrapper = styled.div
|
|
109
|
+
const IconTextWrapper = styled.div`
|
|
117
110
|
display: flex;
|
|
118
111
|
align-items: center;
|
|
119
112
|
gap: ${spacing.xxsmall};
|
|
@@ -125,11 +118,9 @@ const IconTextWrapper = styled.div<LayoutProps>`
|
|
|
125
118
|
}
|
|
126
119
|
${fonts.sizes(16)};
|
|
127
120
|
${mq.range({ until: breakpoints.mobileWide })} {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
display: none;
|
|
132
|
-
`}
|
|
121
|
+
&[data-type='list'] {
|
|
122
|
+
display: none;
|
|
123
|
+
}
|
|
133
124
|
}
|
|
134
125
|
`;
|
|
135
126
|
|
|
@@ -145,7 +136,7 @@ const Count = ({ type, count, layoutType }: IconCountProps) => {
|
|
|
145
136
|
if (!count) return null;
|
|
146
137
|
|
|
147
138
|
return (
|
|
148
|
-
<IconTextWrapper type={layoutType}>
|
|
139
|
+
<IconTextWrapper data-type={layoutType}>
|
|
149
140
|
<Icon />
|
|
150
141
|
<span>{t(`myNdla.${type}s`, { count })}</span>
|
|
151
142
|
</IconTextWrapper>
|
|
@@ -180,22 +171,24 @@ const Folder = ({
|
|
|
180
171
|
const Icon = isShared ? FolderShared : FolderOutlined;
|
|
181
172
|
|
|
182
173
|
return (
|
|
183
|
-
<FolderWrapper type={type} id={id}>
|
|
184
|
-
<TitleWrapper type={type}>
|
|
174
|
+
<FolderWrapper data-type={type} id={id}>
|
|
175
|
+
<TitleWrapper data-type={type}>
|
|
185
176
|
<IconWrapper
|
|
186
177
|
aria-label={`${isShared ? `${t('myNdla.folder.sharing.shared')} ` : ''}${t('myNdla.folder.folder')}`}
|
|
187
178
|
>
|
|
188
179
|
<Icon />
|
|
189
180
|
</IconWrapper>
|
|
190
181
|
<ResourceTitleLink to={link}>
|
|
191
|
-
<FolderTitle title=
|
|
182
|
+
<FolderTitle data-title="" title={title}>
|
|
183
|
+
{title}
|
|
184
|
+
</FolderTitle>
|
|
192
185
|
</ResourceTitleLink>
|
|
193
186
|
</TitleWrapper>
|
|
194
187
|
<MenuWrapper>
|
|
195
188
|
<CountContainer>
|
|
196
189
|
{isShared && (
|
|
197
190
|
// Information regarding the shared status of a folder is read previously, ignore this
|
|
198
|
-
<IconTextWrapper type={type} aria-hidden>
|
|
191
|
+
<IconTextWrapper data-type={type} aria-hidden>
|
|
199
192
|
<Share />
|
|
200
193
|
<span>{t('myNdla.folder.sharing.shared')}</span>
|
|
201
194
|
</IconTextWrapper>
|
|
@@ -24,6 +24,10 @@ interface Props extends ComponentProps<typeof InputV2> {
|
|
|
24
24
|
onSave: () => void;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
const StyledSpinner = styled(Spinner)`
|
|
28
|
+
margin: ${spacing.small};
|
|
29
|
+
`;
|
|
30
|
+
|
|
27
31
|
// Source: https://kovart.github.io/dashed-border-generator/
|
|
28
32
|
const borderStyle = (error?: boolean) =>
|
|
29
33
|
`url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='${encodeURIComponent(
|
|
@@ -102,7 +106,7 @@ const FolderInput = forwardRef<HTMLInputElement, Props>(({ loading, error, onClo
|
|
|
102
106
|
</>
|
|
103
107
|
)}
|
|
104
108
|
<div aria-live="assertive">
|
|
105
|
-
{loading && <
|
|
109
|
+
{loading && <StyledSpinner size="normal" id="folder-spinner" aria-label={t('loading')} />}
|
|
106
110
|
</div>
|
|
107
111
|
</Row>
|
|
108
112
|
}
|
|
@@ -15,11 +15,11 @@ import Image from '../Image';
|
|
|
15
15
|
import {
|
|
16
16
|
CompressedTagList,
|
|
17
17
|
ResourceImageProps,
|
|
18
|
-
ResourceTitle,
|
|
19
18
|
ResourceTypeList,
|
|
20
19
|
ResourceTitleLink,
|
|
21
20
|
LoaderProps,
|
|
22
|
-
|
|
21
|
+
ContentIconWrapper,
|
|
22
|
+
resourceHeadingStyle,
|
|
23
23
|
} from './resourceComponents';
|
|
24
24
|
import ContentLoader from '../ContentLoader';
|
|
25
25
|
import { contentTypeMapping, resourceEmbedTypeMapping } from '../model/ContentType';
|
|
@@ -41,11 +41,23 @@ const BlockElementWrapper = styled.div`
|
|
|
41
41
|
&:hover {
|
|
42
42
|
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
43
43
|
transition-duration: 0.2s;
|
|
44
|
-
|
|
44
|
+
[data-link] {
|
|
45
45
|
color: ${colors.brand.primary};
|
|
46
46
|
text-decoration: underline;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
&:hover,
|
|
51
|
+
&:focus,
|
|
52
|
+
&:focus-within {
|
|
53
|
+
[data-description] {
|
|
54
|
+
/* Unfortunate css needed for multi-line text overflow ellipsis. */
|
|
55
|
+
height: 3.1em;
|
|
56
|
+
-webkit-line-clamp: 2;
|
|
57
|
+
line-clamp: 2;
|
|
58
|
+
-webkit-box-orient: vertical;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
49
61
|
`;
|
|
50
62
|
|
|
51
63
|
const BlockDescription = styled.p`
|
|
@@ -57,14 +69,6 @@ const BlockDescription = styled.p`
|
|
|
57
69
|
overflow: hidden;
|
|
58
70
|
text-overflow: ellipsis;
|
|
59
71
|
transition: height 0.2s ease-out;
|
|
60
|
-
${() => BlockElementWrapper}:hover &, ${() => BlockElementWrapper}:focus & , ${() =>
|
|
61
|
-
BlockElementWrapper}:focus-within & {
|
|
62
|
-
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
63
|
-
height: 3.1em;
|
|
64
|
-
-webkit-line-clamp: 2;
|
|
65
|
-
line-clamp: 2;
|
|
66
|
-
-webkit-box-orient: vertical;
|
|
67
|
-
}
|
|
68
72
|
`;
|
|
69
73
|
|
|
70
74
|
const TagsAndActionMenu = styled.div`
|
|
@@ -114,9 +118,9 @@ const BlockImage = ({ image, loading, contentType }: BlockImageProps) => {
|
|
|
114
118
|
}
|
|
115
119
|
if (image.src === '') {
|
|
116
120
|
return (
|
|
117
|
-
<
|
|
121
|
+
<ContentIconWrapper contentType={contentType}>
|
|
118
122
|
<ContentTypeBadge type={contentType} size="large" />
|
|
119
|
-
</
|
|
123
|
+
</ContentIconWrapper>
|
|
120
124
|
);
|
|
121
125
|
} else {
|
|
122
126
|
return <Image alt={image.alt} src={image.src} fallbackWidth={300} />;
|
|
@@ -161,12 +165,11 @@ const BlockResource = ({
|
|
|
161
165
|
description,
|
|
162
166
|
menuItems,
|
|
163
167
|
isLoading,
|
|
164
|
-
headingLevel = 'h2',
|
|
168
|
+
headingLevel: Heading = 'h2',
|
|
165
169
|
targetBlank,
|
|
166
170
|
resourceTypes,
|
|
167
171
|
}: Props) => {
|
|
168
172
|
const firstResourceType = resourceTypes?.[0]?.id ?? '';
|
|
169
|
-
const Title = ResourceTitle.withComponent(headingLevel);
|
|
170
173
|
|
|
171
174
|
return (
|
|
172
175
|
<BlockElementWrapper id={id}>
|
|
@@ -184,12 +187,12 @@ const BlockResource = ({
|
|
|
184
187
|
<BlockInfoWrapper>
|
|
185
188
|
<ContentWrapper>
|
|
186
189
|
<ResourceTypeAndTitleLoader loading={isLoading}>
|
|
187
|
-
<ResourceTitleLink title={title} target={targetBlank ? '_blank' : undefined} to={link}>
|
|
188
|
-
<
|
|
190
|
+
<ResourceTitleLink data-link="" title={title} target={targetBlank ? '_blank' : undefined} to={link}>
|
|
191
|
+
<Heading css={resourceHeadingStyle}>{title}</Heading>
|
|
189
192
|
</ResourceTitleLink>
|
|
190
193
|
</ResourceTypeAndTitleLoader>
|
|
191
194
|
<ResourceTypeList resourceTypes={resourceTypes} />
|
|
192
|
-
<BlockDescription>{description}</BlockDescription>
|
|
195
|
+
<BlockDescription data-description="">{description}</BlockDescription>
|
|
193
196
|
</ContentWrapper>
|
|
194
197
|
<TagsAndActionMenu>
|
|
195
198
|
{tags && tags.length > 0 && <CompressedTagList tagLinkPrefix={tagLinkPrefix} tags={tags} />}
|
|
@@ -14,11 +14,11 @@ import Image from '../Image';
|
|
|
14
14
|
import {
|
|
15
15
|
CompressedTagList,
|
|
16
16
|
ResourceImageProps,
|
|
17
|
-
|
|
17
|
+
resourceHeadingStyle,
|
|
18
18
|
ResourceTitleLink as StyledLink,
|
|
19
19
|
ResourceTypeList,
|
|
20
|
-
StyledContentIconWrapper,
|
|
21
20
|
LoaderProps,
|
|
21
|
+
ContentIconWrapper,
|
|
22
22
|
} from './resourceComponents';
|
|
23
23
|
import ContentLoader from '../ContentLoader';
|
|
24
24
|
import ContentTypeBadge from '../ContentTypeBadge';
|
|
@@ -48,7 +48,7 @@ const ListResourceWrapper = styled.div`
|
|
|
48
48
|
&:hover {
|
|
49
49
|
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
50
50
|
transition-duration: 0.2s;
|
|
51
|
-
|
|
51
|
+
[data-link] {
|
|
52
52
|
color: ${colors.brand.primary};
|
|
53
53
|
text-decoration: underline;
|
|
54
54
|
}
|
|
@@ -61,11 +61,7 @@ interface StyledImageProps {
|
|
|
61
61
|
|
|
62
62
|
const ImageWrapper = styled.div<StyledImageProps>`
|
|
63
63
|
grid-area: image;
|
|
64
|
-
width:
|
|
65
|
-
${mq.range({ until: breakpoints.mobileWide })} {
|
|
66
|
-
width: 56px;
|
|
67
|
-
margin-bottom: 0;
|
|
68
|
-
}
|
|
64
|
+
width: 56px;
|
|
69
65
|
overflow: hidden;
|
|
70
66
|
border-radius: 2px;
|
|
71
67
|
display: flex;
|
|
@@ -73,6 +69,13 @@ const ImageWrapper = styled.div<StyledImageProps>`
|
|
|
73
69
|
align-items: center;
|
|
74
70
|
justify-content: center;
|
|
75
71
|
aspect-ratio: 4/3;
|
|
72
|
+
&[data-image-size='normal'] {
|
|
73
|
+
width: 136px;
|
|
74
|
+
}
|
|
75
|
+
${mq.range({ until: breakpoints.mobileWide })} {
|
|
76
|
+
width: 56px;
|
|
77
|
+
margin-bottom: 0;
|
|
78
|
+
}
|
|
76
79
|
`;
|
|
77
80
|
|
|
78
81
|
const StyledImage = styled(Image)`
|
|
@@ -138,9 +141,9 @@ const ListResourceImage = ({ resourceImage, loading, type, contentType, backgrou
|
|
|
138
141
|
if (!loading) {
|
|
139
142
|
if (resourceImage.src === '') {
|
|
140
143
|
return (
|
|
141
|
-
<
|
|
144
|
+
<ContentIconWrapper contentType={contentType}>
|
|
142
145
|
<ContentTypeBadge type={contentType} background={background} size="x-small" />
|
|
143
|
-
</
|
|
146
|
+
</ContentIconWrapper>
|
|
144
147
|
);
|
|
145
148
|
} else {
|
|
146
149
|
return (
|
|
@@ -237,8 +240,10 @@ const ListResource = ({
|
|
|
237
240
|
</ImageWrapper>
|
|
238
241
|
<TopicAndTitleWrapper>
|
|
239
242
|
<TypeAndTitleLoader loading={isLoading}>
|
|
240
|
-
<StyledLink to={link} target={targetBlank ? '_blank' : undefined}>
|
|
241
|
-
<
|
|
243
|
+
<StyledLink to={link} data-link="" target={targetBlank ? '_blank' : undefined}>
|
|
244
|
+
<h1 css={resourceHeadingStyle} title={title}>
|
|
245
|
+
{title}
|
|
246
|
+
</h1>
|
|
242
247
|
</StyledLink>
|
|
243
248
|
<ResourceTypeList resourceTypes={resourceTypes} />
|
|
244
249
|
</TypeAndTitleLoader>
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
import styled from '@emotion/styled';
|
|
10
10
|
import { colors, fonts, spacing } from '@ndla/core';
|
|
11
|
-
import React, { ReactNode } from 'react';
|
|
11
|
+
import React, { CSSProperties, HTMLAttributes, ReactNode, useMemo } from 'react';
|
|
12
12
|
import { useTranslation } from 'react-i18next';
|
|
13
13
|
import { MenuButton } from '@ndla/button';
|
|
14
14
|
import SafeLink from '@ndla/safelink';
|
|
15
15
|
import { useNavigate } from 'react-router-dom';
|
|
16
16
|
import { HashTag } from '@ndla/icons/common';
|
|
17
|
+
import { css } from '@emotion/react';
|
|
17
18
|
import resourceTypeColor from '../utils/resourceTypeColor';
|
|
18
19
|
import { resourceEmbedTypeMapping } from '../model/ContentType';
|
|
19
20
|
|
|
@@ -37,7 +38,7 @@ export const ResourceTitleLink = styled(SafeLink)`
|
|
|
37
38
|
}
|
|
38
39
|
`;
|
|
39
40
|
|
|
40
|
-
export const
|
|
41
|
+
export const resourceHeadingStyle = css`
|
|
41
42
|
margin: 0;
|
|
42
43
|
overflow: hidden;
|
|
43
44
|
text-overflow: ellipsis;
|
|
@@ -113,19 +114,32 @@ const TagCounterWrapper = styled.span`
|
|
|
113
114
|
${fonts.sizes('14px', '14px')};
|
|
114
115
|
`;
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
interface ContentIconProps extends HTMLAttributes<HTMLSpanElement> {
|
|
117
118
|
contentType: string;
|
|
119
|
+
children?: ReactNode;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
|
|
122
|
+
const StyledContentIconWrapper = styled.span`
|
|
121
123
|
width: 100%;
|
|
122
124
|
aspect-ratio: 4/3;
|
|
123
125
|
display: flex;
|
|
124
126
|
align-items: center;
|
|
125
127
|
justify-content: center;
|
|
126
|
-
background-color:
|
|
128
|
+
background-color: var(--content-background-color);
|
|
127
129
|
`;
|
|
128
130
|
|
|
131
|
+
export const ContentIconWrapper = ({ contentType, children, ...props }: ContentIconProps) => {
|
|
132
|
+
const contentIconWrapperVars = useMemo(
|
|
133
|
+
() => ({ '--content-background-color': resourceTypeColor(contentType) } as unknown as CSSProperties),
|
|
134
|
+
[contentType],
|
|
135
|
+
);
|
|
136
|
+
return (
|
|
137
|
+
<StyledContentIconWrapper {...props} style={contentIconWrapperVars}>
|
|
138
|
+
{children}
|
|
139
|
+
</StyledContentIconWrapper>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
129
143
|
interface TagListProps {
|
|
130
144
|
tags?: string[];
|
|
131
145
|
tagLinkPrefix?: string;
|
|
@@ -161,16 +175,21 @@ interface CompressedTagListProps {
|
|
|
161
175
|
export const CompressedTagList = ({ tags, tagLinkPrefix }: CompressedTagListProps) => {
|
|
162
176
|
const navigate = useNavigate();
|
|
163
177
|
const { t } = useTranslation();
|
|
164
|
-
const visibleTags = tags.slice(0, 3);
|
|
165
|
-
const remainingTags =
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
178
|
+
const visibleTags = useMemo(() => tags.slice(0, 3), [tags]);
|
|
179
|
+
const remainingTags = useMemo(
|
|
180
|
+
() =>
|
|
181
|
+
tags.slice(3, tags.length).map((tag) => {
|
|
182
|
+
return {
|
|
183
|
+
icon: <HashTag />,
|
|
184
|
+
text: tag,
|
|
185
|
+
onClick: () => {
|
|
186
|
+
navigate(`${tagLinkPrefix ? tagLinkPrefix : ''}/${encodeURIComponent(tag)}`);
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}),
|
|
190
|
+
[navigate, tagLinkPrefix, tags],
|
|
191
|
+
);
|
|
192
|
+
|
|
174
193
|
return (
|
|
175
194
|
<>
|
|
176
195
|
<TagList tagLinkPrefix={tagLinkPrefix} tags={visibleTags} />
|
|
@@ -18,15 +18,13 @@ import { TreeStructureType } from './types';
|
|
|
18
18
|
import { arrowNavigation } from './arrowNavigation';
|
|
19
19
|
import ContentLoader from '../ContentLoader';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
isOpen: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const StyledRow = styled.div<StyledRowProps>`
|
|
21
|
+
const StyledRow = styled.div`
|
|
26
22
|
display: flex;
|
|
27
23
|
padding: ${spacing.xxsmall};
|
|
28
24
|
align-items: center;
|
|
29
|
-
|
|
25
|
+
&[data-open='true'] {
|
|
26
|
+
border-bottom: 1px solid ${colors.brand.tertiary};
|
|
27
|
+
}
|
|
30
28
|
`;
|
|
31
29
|
const StyledSelectedFolder = styled(Button)`
|
|
32
30
|
flex: 1;
|
|
@@ -110,7 +108,7 @@ const ComboboxButton = forwardRef<HTMLButtonElement, Props>(
|
|
|
110
108
|
|
|
111
109
|
return (
|
|
112
110
|
<StyledRow
|
|
113
|
-
|
|
111
|
+
data-open={showTree}
|
|
114
112
|
onMouseDown={(e) => {
|
|
115
113
|
if (!e.defaultPrevented) {
|
|
116
114
|
e.preventDefault();
|