@ndla/ui 14.0.0 → 15.0.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/Article/Article.js +22 -3
- package/es/Article/ArticleFavoritesButton.js +38 -0
- package/es/Article/index.js +2 -1
- package/es/Breadcrumb/ActionBreadcrumb.js +57 -0
- package/es/Breadcrumb/index.js +1 -0
- package/es/InfoBlock/InfoBlock.js +55 -0
- package/es/InfoBlock/index.js +1 -0
- package/es/MyNdla/Navigation/VerticalNavigation.js +51 -0
- package/es/MyNdla/Navigation/index.js +2 -0
- package/es/MyNdla/Resource/Folder.js +86 -0
- package/{lib/MyNdla/ResourceDash/ResourcesView.d.ts → es/MyNdla/Resource/index.js} +2 -3
- package/es/MyNdla/index.js +3 -4
- package/es/Resource/BlockResource.js +73 -0
- package/es/Resource/ListResource.js +66 -0
- package/es/Resource/index.js +10 -0
- package/es/Resource/resourceComponents.js +97 -0
- package/es/ResourceGroup/ResourceGroup.js +7 -5
- package/es/ResourceGroup/ResourceItem.js +25 -24
- package/es/ResourceGroup/ResourceList.js +18 -6
- package/es/SnackBar/SnackBar.js +117 -0
- package/es/SnackBar/index.js +9 -0
- package/es/TagSelector/SuggestionInput.js +240 -0
- package/es/TagSelector/Suggestions.js +93 -0
- package/es/TagSelector/TagSelector.js +137 -0
- package/es/TagSelector/index.js +9 -0
- package/es/TreeStructure/FolderItem.js +130 -0
- package/es/TreeStructure/FolderItems.js +123 -0
- package/es/TreeStructure/FolderNameInput.js +112 -0
- package/es/TreeStructure/TreeStructure.js +254 -0
- package/es/TreeStructure/TreeStructure.types.js +0 -0
- package/es/TreeStructure/TreeStructureWrapper.js +13 -0
- package/es/TreeStructure/helperFunctions.js +92 -0
- package/es/TreeStructure/index.js +9 -0
- package/es/TreeStructure/keyboardNavigation/keyboardNavigation.js +182 -0
- package/es/TreeStructure/keyboardNavigation/keyboardNavigation.types.js +0 -0
- package/es/all.css +72 -0
- package/es/index.js +8 -3
- package/es/locale/messages-en.js +62 -4
- package/es/locale/messages-nb.js +61 -3
- package/es/locale/messages-nn.js +61 -3
- package/es/locale/messages-se.js +61 -3
- package/es/locale/messages-sma.js +61 -3
- package/lib/Article/Article.d.ts +3 -1
- package/lib/Article/Article.js +43 -23
- package/lib/Article/ArticleFavoritesButton.d.ts +15 -0
- package/lib/Article/ArticleFavoritesButton.js +56 -0
- package/lib/Article/index.d.ts +2 -1
- package/lib/Article/index.js +8 -0
- package/lib/Breadcrumb/ActionBreadcrumb.d.ts +16 -0
- package/lib/Breadcrumb/ActionBreadcrumb.js +72 -0
- package/lib/Breadcrumb/index.d.ts +1 -0
- package/lib/Breadcrumb/index.js +8 -0
- package/lib/InfoBlock/InfoBlock.d.ts +8 -0
- package/lib/InfoBlock/InfoBlock.js +58 -0
- package/lib/InfoBlock/index.d.ts +1 -0
- package/lib/InfoBlock/index.js +13 -0
- package/lib/MyNdla/Navigation/VerticalNavigation.d.ts +10 -0
- package/lib/MyNdla/Navigation/VerticalNavigation.js +61 -0
- package/lib/MyNdla/Navigation/index.d.ts +2 -0
- package/lib/MyNdla/Navigation/index.js +15 -0
- package/lib/MyNdla/Resource/Folder.d.ts +20 -0
- package/lib/MyNdla/Resource/Folder.js +100 -0
- package/lib/MyNdla/Resource/index.d.ts +9 -0
- package/lib/MyNdla/Resource/index.js +15 -0
- package/lib/MyNdla/index.d.ts +3 -4
- package/lib/MyNdla/index.js +9 -11
- package/lib/Resource/BlockResource.d.ts +20 -0
- package/lib/Resource/BlockResource.js +84 -0
- package/lib/Resource/ListResource.d.ts +20 -0
- package/lib/Resource/ListResource.js +78 -0
- package/lib/Resource/index.d.ts +11 -0
- package/lib/Resource/index.js +29 -0
- package/lib/Resource/resourceComponents.d.ts +24 -0
- package/lib/Resource/resourceComponents.js +106 -0
- package/lib/ResourceGroup/ResourceGroup.d.ts +2 -1
- package/lib/ResourceGroup/ResourceGroup.js +7 -5
- package/lib/ResourceGroup/ResourceItem.d.ts +5 -1
- package/lib/ResourceGroup/ResourceItem.js +26 -24
- package/lib/ResourceGroup/ResourceList.d.ts +3 -1
- package/lib/ResourceGroup/ResourceList.js +18 -6
- package/lib/SnackBar/SnackBar.d.ts +23 -0
- package/lib/SnackBar/SnackBar.js +127 -0
- package/lib/SnackBar/index.d.ts +10 -0
- package/lib/SnackBar/index.js +15 -0
- package/lib/TagSelector/SuggestionInput.d.ts +19 -0
- package/lib/TagSelector/SuggestionInput.js +255 -0
- package/lib/TagSelector/Suggestions.d.ts +12 -0
- package/lib/TagSelector/Suggestions.js +96 -0
- package/lib/TagSelector/TagSelector.d.ts +16 -0
- package/lib/TagSelector/TagSelector.js +150 -0
- package/lib/TagSelector/index.d.ts +10 -0
- package/lib/TagSelector/index.js +19 -0
- package/lib/TreeStructure/FolderItem.d.ts +27 -0
- package/lib/TreeStructure/FolderItem.js +140 -0
- package/lib/TreeStructure/FolderItems.d.ts +11 -0
- package/lib/TreeStructure/FolderItems.js +130 -0
- package/lib/TreeStructure/FolderNameInput.d.ts +15 -0
- package/lib/TreeStructure/FolderNameInput.js +125 -0
- package/lib/TreeStructure/TreeStructure.d.ts +12 -0
- package/lib/TreeStructure/TreeStructure.js +273 -0
- package/lib/TreeStructure/TreeStructure.types.d.ts +63 -0
- package/lib/TreeStructure/TreeStructure.types.js +1 -0
- package/lib/TreeStructure/TreeStructureWrapper.d.ts +12 -0
- package/lib/TreeStructure/TreeStructureWrapper.js +24 -0
- package/lib/TreeStructure/helperFunctions.d.ts +5 -0
- package/lib/TreeStructure/helperFunctions.js +103 -0
- package/lib/TreeStructure/index.d.ts +10 -0
- package/lib/TreeStructure/index.js +15 -0
- package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.d.ts +11 -0
- package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.js +186 -0
- package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.types.d.ts +26 -0
- package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.types.js +1 -0
- package/lib/User/apiTypes.d.ts +1 -1
- package/lib/User/index.d.ts +2 -2
- package/lib/all.css +72 -0
- package/lib/index.d.ts +13 -4
- package/lib/index.js +68 -9
- package/lib/locale/messages-en.d.ts +58 -0
- package/lib/locale/messages-en.js +62 -4
- package/lib/locale/messages-nb.d.ts +58 -0
- package/lib/locale/messages-nb.js +61 -3
- package/lib/locale/messages-nn.d.ts +58 -0
- package/lib/locale/messages-nn.js +61 -3
- package/lib/locale/messages-se.d.ts +58 -0
- package/lib/locale/messages-se.js +61 -3
- package/lib/locale/messages-sma.d.ts +58 -0
- package/lib/locale/messages-sma.js +61 -3
- package/lib/types.d.ts +1 -1
- package/package.json +11 -11
- package/src/Article/Article.tsx +31 -0
- package/src/Article/ArticleFavoritesButton.tsx +40 -0
- package/src/Article/index.ts +2 -0
- package/src/Breadcrumb/ActionBreadcrumb.tsx +68 -0
- package/src/Breadcrumb/index.ts +2 -0
- package/src/InfoBlock/InfoBlock.tsx +61 -0
- package/src/InfoBlock/index.ts +1 -0
- package/src/MyNdla/Navigation/VerticalNavigation.tsx +93 -0
- package/src/MyNdla/Navigation/index.ts +2 -0
- package/src/MyNdla/Resource/Folder.tsx +143 -0
- package/src/MyNdla/Resource/index.ts +10 -0
- package/src/MyNdla/index.ts +3 -5
- package/src/Resource/BlockResource.tsx +101 -0
- package/src/Resource/ListResource.tsx +111 -0
- package/src/Resource/index.ts +12 -0
- package/src/Resource/resourceComponents.tsx +143 -0
- package/src/ResourceGroup/ResourceGroup.tsx +3 -0
- package/src/ResourceGroup/ResourceItem.tsx +17 -0
- package/src/ResourceGroup/ResourceList.tsx +16 -3
- package/src/SnackBar/SnackBar.tsx +183 -0
- package/src/SnackBar/index.ts +13 -0
- package/src/TagSelector/SuggestionInput.tsx +230 -0
- package/src/TagSelector/Suggestions.tsx +125 -0
- package/src/TagSelector/TagSelector.tsx +111 -0
- package/src/TagSelector/index.ts +13 -0
- package/src/TreeStructure/FolderItem.tsx +160 -0
- package/src/TreeStructure/FolderItems.tsx +109 -0
- package/src/TreeStructure/FolderNameInput.tsx +109 -0
- package/src/TreeStructure/TreeStructure.tsx +184 -0
- package/src/TreeStructure/TreeStructure.types.ts +69 -0
- package/src/TreeStructure/TreeStructureWrapper.tsx +34 -0
- package/src/TreeStructure/helperFunctions.ts +52 -0
- package/src/TreeStructure/index.ts +11 -0
- package/src/TreeStructure/keyboardNavigation/keyboardNavigation.ts +161 -0
- package/src/TreeStructure/keyboardNavigation/keyboardNavigation.types.ts +28 -0
- package/src/User/apiTypes.ts +1 -1
- package/src/User/index.ts +2 -2
- package/src/all.scss +1 -0
- package/src/index.ts +14 -5
- package/src/locale/messages-en.ts +56 -3
- package/src/locale/messages-nb.ts +55 -2
- package/src/locale/messages-nn.ts +55 -2
- package/src/locale/messages-se.ts +55 -2
- package/src/locale/messages-sma.ts +55 -2
- package/src/types.ts +1 -1
- package/es/MyNdla/ResourceDash/Breadcrumbs.js +0 -22
- package/es/MyNdla/ResourceDash/ResourceElement.js +0 -27
- package/es/MyNdla/ResourceDash/ResourcesView.js +0 -43
- package/es/MyNdla/ResourceDash/index.js +0 -4
- package/lib/MyNdla/ResourceDash/Breadcrumbs.d.ts +0 -15
- package/lib/MyNdla/ResourceDash/Breadcrumbs.js +0 -35
- package/lib/MyNdla/ResourceDash/ResourceElement.d.ts +0 -18
- package/lib/MyNdla/ResourceDash/ResourceElement.js +0 -38
- package/lib/MyNdla/ResourceDash/ResourcesView.js +0 -57
- package/lib/MyNdla/ResourceDash/index.d.ts +0 -4
- package/lib/MyNdla/ResourceDash/index.js +0 -31
- package/src/MyNdla/ResourceDash/Breadcrumbs.tsx +0 -31
- package/src/MyNdla/ResourceDash/ResourceElement.tsx +0 -50
- package/src/MyNdla/ResourceDash/ResourcesView.tsx +0 -42
- package/src/MyNdla/ResourceDash/index.ts +0 -5
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import styled from '@emotion/styled';
|
|
10
|
+
import React, { ReactNode } from 'react';
|
|
11
|
+
import { FolderOutlined } from '@ndla/icons/contentType';
|
|
12
|
+
import { FileDocumentOutline } from '@ndla/icons/common';
|
|
13
|
+
import { fonts, spacing, colors } from '@ndla/core';
|
|
14
|
+
import { css } from '@emotion/core';
|
|
15
|
+
import { useTranslation } from 'react-i18next';
|
|
16
|
+
import SafeLink from '@ndla/safelink';
|
|
17
|
+
import { MenuButton } from '@ndla/button';
|
|
18
|
+
|
|
19
|
+
interface FolderIconWrapperProps {
|
|
20
|
+
type?: LayoutType;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const FolderIconWrapper = styled.div<FolderIconWrapperProps>`
|
|
24
|
+
display: flex;
|
|
25
|
+
border-radius: 100%;
|
|
26
|
+
padding: 11px;
|
|
27
|
+
background-color: ${colors.brand.greyLighter};
|
|
28
|
+
svg {
|
|
29
|
+
width: 18px;
|
|
30
|
+
height: 18px;
|
|
31
|
+
}
|
|
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
|
+
`;
|
|
42
|
+
|
|
43
|
+
const FolderTitle = styled.h2`
|
|
44
|
+
${fonts.sizes(18)};
|
|
45
|
+
font-weight: ${fonts.weight.normal};
|
|
46
|
+
margin: 0;
|
|
47
|
+
flex: 1;
|
|
48
|
+
|
|
49
|
+
overflow: hidden;
|
|
50
|
+
text-overflow: ellipsis;
|
|
51
|
+
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
52
|
+
display: -webkit-box;
|
|
53
|
+
-webkit-line-clamp: 1;
|
|
54
|
+
line-clamp: 1;
|
|
55
|
+
-webkit-box-orient: vertical;
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
const FolderWrapper = styled(SafeLink)`
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
padding: ${spacing.small};
|
|
62
|
+
border: 1px solid ${colors.brand.light};
|
|
63
|
+
border-radius: 2px;
|
|
64
|
+
box-shadow: none;
|
|
65
|
+
text-decoration: none;
|
|
66
|
+
color: ${colors.brand.greyDark};
|
|
67
|
+
font-family: ${fonts.sans};
|
|
68
|
+
transition-duration: 0.2s;
|
|
69
|
+
gap: ${spacing.small};
|
|
70
|
+
&:hover {
|
|
71
|
+
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
72
|
+
transition-duration: 0.2s;
|
|
73
|
+
${FolderTitle} {
|
|
74
|
+
color: ${colors.brand.primary};
|
|
75
|
+
text-decoration: underline;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
interface Props {
|
|
81
|
+
title: string;
|
|
82
|
+
subFolders?: number;
|
|
83
|
+
subResources?: number;
|
|
84
|
+
description?: string;
|
|
85
|
+
link: string;
|
|
86
|
+
type: LayoutType;
|
|
87
|
+
actionMenu?: ReactNode;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface IconCountProps {
|
|
91
|
+
type: 'resource' | 'folder';
|
|
92
|
+
count?: number;
|
|
93
|
+
layoutType: LayoutType;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface IconCountWrapperProps {
|
|
97
|
+
type: LayoutType;
|
|
98
|
+
}
|
|
99
|
+
const IconCountWrapper = styled.div<IconCountWrapperProps>`
|
|
100
|
+
display: flex;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 2px;
|
|
103
|
+
${fonts.sizes(16)};
|
|
104
|
+
${(p) =>
|
|
105
|
+
p.type === 'block' &&
|
|
106
|
+
css`
|
|
107
|
+
opacity: 0;
|
|
108
|
+
${FolderWrapper}:hover & {
|
|
109
|
+
opacity: 1;
|
|
110
|
+
}
|
|
111
|
+
`};
|
|
112
|
+
`;
|
|
113
|
+
const IconCount = ({ type, count, layoutType }: IconCountProps) => {
|
|
114
|
+
const Icon = type === 'resource' ? FileDocumentOutline : FolderOutlined;
|
|
115
|
+
const { t } = useTranslation();
|
|
116
|
+
if (!count) return null;
|
|
117
|
+
|
|
118
|
+
return (
|
|
119
|
+
<IconCountWrapper type={layoutType}>
|
|
120
|
+
<Icon aria-label={t(`myNdla.${type}s`)} />
|
|
121
|
+
<span>{layoutType === 'block' ? count : t(`myNdla.${type}s`, { count: 3 })}</span>
|
|
122
|
+
</IconCountWrapper>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
type LayoutType = 'list' | 'block';
|
|
127
|
+
|
|
128
|
+
const Folder = ({ link, title, subFolders, subResources, type = 'list', actionMenu }: Props) => {
|
|
129
|
+
const { t } = useTranslation();
|
|
130
|
+
return (
|
|
131
|
+
<FolderWrapper to={link}>
|
|
132
|
+
<FolderIconWrapper type={type}>
|
|
133
|
+
<FolderOutlined aria-label={t('myNdla.folder')} />
|
|
134
|
+
</FolderIconWrapper>
|
|
135
|
+
<FolderTitle>{title}</FolderTitle>
|
|
136
|
+
<IconCount layoutType={type} type={'folder'} count={subFolders} />
|
|
137
|
+
<IconCount layoutType={type} type={'resource'} count={subResources} />
|
|
138
|
+
<MenuButton size="small" />
|
|
139
|
+
</FolderWrapper>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export default Folder;
|
package/src/MyNdla/index.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export { ResourceElement, ResourcesView, Breadcrumbs };
|
|
1
|
+
import Folder from './Resource/Folder';
|
|
2
|
+
import { VerticalNavigation } from './Navigation';
|
|
3
|
+
export { VerticalNavigation, Folder };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import styled from '@emotion/styled';
|
|
10
|
+
import React, { ReactNode } from 'react';
|
|
11
|
+
import SafeLink from '@ndla/safelink';
|
|
12
|
+
import { colors, fonts, spacing } from '@ndla/core';
|
|
13
|
+
import Image from '../Image';
|
|
14
|
+
import { CompressTagsLength, ResourceImageProps, ResourceTitle, Row, TopicList } from './resourceComponents';
|
|
15
|
+
|
|
16
|
+
interface BlockResourceProps {
|
|
17
|
+
link: string;
|
|
18
|
+
title: string;
|
|
19
|
+
resourceImage: ResourceImageProps;
|
|
20
|
+
topics: string[];
|
|
21
|
+
tags?: string[];
|
|
22
|
+
description?: string;
|
|
23
|
+
actionMenu?: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const BlockElementWrapper = styled(SafeLink)`
|
|
27
|
+
display: flex;
|
|
28
|
+
text-decoration: none;
|
|
29
|
+
box-shadow: none;
|
|
30
|
+
flex-direction: column;
|
|
31
|
+
max-width: 300px;
|
|
32
|
+
max-height: 240px;
|
|
33
|
+
border: 1px solid ${colors.brand.light};
|
|
34
|
+
border-radius: 2px;
|
|
35
|
+
color: ${colors.brand.greyDark};
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const BlockDescription = styled.p`
|
|
39
|
+
display: -webkit-box;
|
|
40
|
+
line-clamp: 2;
|
|
41
|
+
${fonts.sizes(16)};
|
|
42
|
+
height: 0em;
|
|
43
|
+
margin: 0;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
text-overflow: ellipsis;
|
|
46
|
+
transition: height 0.2s ease-out;
|
|
47
|
+
${() => BlockElementWrapper}:hover & {
|
|
48
|
+
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
49
|
+
height: 3.1em;
|
|
50
|
+
-webkit-line-clamp: 2;
|
|
51
|
+
line-clamp: 2;
|
|
52
|
+
-webkit-box-orient: vertical;
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const RightRow = styled(Row)`
|
|
57
|
+
justify-content: flex-end;
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
const BlockInfoWrapper = styled.div`
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
padding: ${spacing.small};
|
|
64
|
+
gap: ${spacing.xxsmall};
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const ImageWrapper = styled.div`
|
|
68
|
+
display: flex;
|
|
69
|
+
width: 100%;
|
|
70
|
+
overflow: hidden;
|
|
71
|
+
align-items: center;
|
|
72
|
+
div {
|
|
73
|
+
min-width: 100%;
|
|
74
|
+
}
|
|
75
|
+
img {
|
|
76
|
+
min-width: 100%;
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const BlockResource = ({ link, title, tags, resourceImage, topics, description, actionMenu }: BlockResourceProps) => {
|
|
81
|
+
return (
|
|
82
|
+
<BlockElementWrapper to={link}>
|
|
83
|
+
<ImageWrapper>
|
|
84
|
+
<Image alt={resourceImage.alt} src={resourceImage.src} />
|
|
85
|
+
</ImageWrapper>
|
|
86
|
+
<BlockInfoWrapper>
|
|
87
|
+
<div>
|
|
88
|
+
<ResourceTitle>{title}</ResourceTitle>
|
|
89
|
+
</div>
|
|
90
|
+
<TopicList topics={topics} />
|
|
91
|
+
<BlockDescription>{description}</BlockDescription>
|
|
92
|
+
<RightRow>
|
|
93
|
+
{tags && CompressTagsLength(tags)}
|
|
94
|
+
{actionMenu}
|
|
95
|
+
</RightRow>
|
|
96
|
+
</BlockInfoWrapper>
|
|
97
|
+
</BlockElementWrapper>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export default BlockResource;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import styled from '@emotion/styled';
|
|
10
|
+
import React, { ReactNode } from 'react';
|
|
11
|
+
import SafeLink from '@ndla/safelink';
|
|
12
|
+
import { fonts, spacing, colors } from '@ndla/core';
|
|
13
|
+
import Image from '../Image';
|
|
14
|
+
import { CompressTagsLength, ResourceImageProps, ResourceTitle, Row, TopicList } from './resourceComponents';
|
|
15
|
+
|
|
16
|
+
const ResourceDescription = styled.p`
|
|
17
|
+
line-clamp: 2;
|
|
18
|
+
line-height: 1em;
|
|
19
|
+
height: 3.1em;
|
|
20
|
+
margin: 0;
|
|
21
|
+
overflow: hidden;
|
|
22
|
+
${fonts.sizes(16)};
|
|
23
|
+
text-overflow: ellipsis;
|
|
24
|
+
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
25
|
+
display: -webkit-box;
|
|
26
|
+
-webkit-line-clamp: 2;
|
|
27
|
+
line-clamp: 2;
|
|
28
|
+
-webkit-box-orient: vertical;
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const ResourceWrapper = styled(SafeLink)`
|
|
32
|
+
display: grid;
|
|
33
|
+
grid-template-columns: auto 1fr;
|
|
34
|
+
text-decoration: none;
|
|
35
|
+
box-shadow: none;
|
|
36
|
+
padding: ${spacing.small};
|
|
37
|
+
border: 1px solid ${colors.brand.light};
|
|
38
|
+
border-radius: 2px;
|
|
39
|
+
color: ${colors.brand.greyDark};
|
|
40
|
+
gap: ${spacing.small};
|
|
41
|
+
&:hover {
|
|
42
|
+
box-shadow: 1px 1px 6px 2px rgba(9, 55, 101, 0.08);
|
|
43
|
+
transition-duration: 0.2s;
|
|
44
|
+
${ResourceTitle} {
|
|
45
|
+
color: ${colors.brand.primary};
|
|
46
|
+
text-decoration: underline;
|
|
47
|
+
}
|
|
48
|
+
a {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const ResourceInfoWrapper = styled.div`
|
|
56
|
+
flex: 1;
|
|
57
|
+
display: flex;
|
|
58
|
+
flex-direction: column;
|
|
59
|
+
max-width: 100%;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
interface StyledImageProps {
|
|
64
|
+
imageSize: 'normal' | 'compact';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const StyledImage = styled(Image)<StyledImageProps>`
|
|
68
|
+
display: flex;
|
|
69
|
+
border-radius: 2px;
|
|
70
|
+
object-fit: cover;
|
|
71
|
+
width: ${(p) => (p.imageSize === 'normal' ? '136px' : '56px')};
|
|
72
|
+
min-width: ${(p) => (p.imageSize === 'normal' ? '136px' : '56px')};
|
|
73
|
+
height: ${(p) => (p.imageSize === 'normal' ? '96px' : '40px')};
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
export interface ListResourceProps {
|
|
77
|
+
link: string;
|
|
78
|
+
title: string;
|
|
79
|
+
resourceImage: ResourceImageProps;
|
|
80
|
+
topics: string[];
|
|
81
|
+
tags?: string[];
|
|
82
|
+
description?: string;
|
|
83
|
+
actionMenu?: ReactNode;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const ListResource = ({ link, title, tags, resourceImage, topics, description, actionMenu }: ListResourceProps) => {
|
|
87
|
+
const showDescription = description !== undefined;
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<ResourceWrapper to={link}>
|
|
91
|
+
<StyledImage alt={resourceImage.alt} src={resourceImage.src} imageSize={showDescription ? 'normal' : 'compact'} />
|
|
92
|
+
<ResourceInfoWrapper>
|
|
93
|
+
<Row>
|
|
94
|
+
<ResourceTitle>{title}</ResourceTitle>
|
|
95
|
+
{tags && CompressTagsLength(tags)}
|
|
96
|
+
{actionMenu}
|
|
97
|
+
</Row>
|
|
98
|
+
<Row>
|
|
99
|
+
<TopicList topics={topics} />
|
|
100
|
+
</Row>
|
|
101
|
+
{showDescription && (
|
|
102
|
+
<Row>
|
|
103
|
+
<ResourceDescription>{description}</ResourceDescription>
|
|
104
|
+
</Row>
|
|
105
|
+
)}
|
|
106
|
+
</ResourceInfoWrapper>
|
|
107
|
+
</ResourceWrapper>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default ListResource;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ListResourceProps } from './ListResource';
|
|
10
|
+
export type { ListResourceProps };
|
|
11
|
+
export { default as ListResource } from './ListResource';
|
|
12
|
+
export { default as BlockResource } from './BlockResource';
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2022-present, NDLA.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the GPLv3 license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import styled from '@emotion/styled';
|
|
11
|
+
import { fonts, colors, spacing } from '@ndla/core';
|
|
12
|
+
|
|
13
|
+
import { MenuButton } from '@ndla/button';
|
|
14
|
+
|
|
15
|
+
export interface ResourceImageProps {
|
|
16
|
+
alt: string;
|
|
17
|
+
src: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const ResourceTitle = styled.h2`
|
|
21
|
+
${fonts.sizes(18)};
|
|
22
|
+
min-width: 50px;
|
|
23
|
+
font-weight: ${fonts.weight.bold};
|
|
24
|
+
margin: 0;
|
|
25
|
+
flex: 1;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
text-overflow: ellipsis;
|
|
28
|
+
// Unfortunate css needed for multi-line text overflow ellipsis.
|
|
29
|
+
line-height: 1;
|
|
30
|
+
display: -webkit-box;
|
|
31
|
+
-webkit-line-clamp: 1;
|
|
32
|
+
line-clamp: 1;
|
|
33
|
+
-webkit-box-orient: vertical;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const StyledTagList = styled.ul`
|
|
37
|
+
list-style: none;
|
|
38
|
+
display: flex;
|
|
39
|
+
margin: 0;
|
|
40
|
+
padding: 0;
|
|
41
|
+
gap: ${spacing.xsmall};
|
|
42
|
+
overflow: hidden;
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const StyledTagListElement = styled.li`
|
|
46
|
+
color: ${colors.brand.grey};
|
|
47
|
+
margin: 0;
|
|
48
|
+
${fonts.sizes(14)};
|
|
49
|
+
::before {
|
|
50
|
+
content: '#';
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
const StyledTopicList = styled.ul`
|
|
55
|
+
list-style: none;
|
|
56
|
+
display: flex;
|
|
57
|
+
margin: 0;
|
|
58
|
+
padding: 0;
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const StyledTopicListElement = styled.li`
|
|
63
|
+
${fonts.sizes(12)};
|
|
64
|
+
margin: 0;
|
|
65
|
+
line-height: 1.5;
|
|
66
|
+
padding: 0;
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
const StyledTopicDivider = styled.span`
|
|
70
|
+
margin: 0;
|
|
71
|
+
padding: 0 ${spacing.xxsmall};
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
export const Row = styled.div`
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: ${spacing.xsmall};
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const TagCounterWrapper = styled.p`
|
|
81
|
+
color: ${colors.brand.primary};
|
|
82
|
+
box-shadow: none;
|
|
83
|
+
margin: 0;
|
|
84
|
+
font-weight: ${fonts.weight.semibold};
|
|
85
|
+
${fonts.sizes(16)}
|
|
86
|
+
`;
|
|
87
|
+
|
|
88
|
+
interface TagListProps {
|
|
89
|
+
tags?: string[];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export const TagList = ({ tags }: TagListProps) => {
|
|
93
|
+
if (!tags) return null;
|
|
94
|
+
return (
|
|
95
|
+
<StyledTagList>
|
|
96
|
+
{tags.map((tag, i) => (
|
|
97
|
+
<StyledTagListElement key={`tag-${i}`}>{tag}</StyledTagListElement>
|
|
98
|
+
))}
|
|
99
|
+
</StyledTagList>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const CompressTagsLength = (tags: string[]) => {
|
|
104
|
+
const tagCounter = tags?.length - 3;
|
|
105
|
+
const slicedTags = tags.slice(0, 3);
|
|
106
|
+
const remainingTags = tags.slice(3, tags.length).map((tag) => {
|
|
107
|
+
return {
|
|
108
|
+
text: '#' + tag,
|
|
109
|
+
onClick: () => {},
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<>
|
|
115
|
+
<TagList tags={slicedTags} />
|
|
116
|
+
{remainingTags.length > 0 && (
|
|
117
|
+
<MenuButton hideMenuIcon={true} menuItems={remainingTags}>
|
|
118
|
+
<TagCounterWrapper>+ {tagCounter}</TagCounterWrapper>
|
|
119
|
+
</MenuButton>
|
|
120
|
+
)}
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
interface TopicListProps {
|
|
126
|
+
topics?: string[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const TopicList = ({ topics }: TopicListProps) => {
|
|
130
|
+
if (!topics) return null;
|
|
131
|
+
return (
|
|
132
|
+
<StyledTopicList>
|
|
133
|
+
{topics.map((topic, i) => (
|
|
134
|
+
<StyledTopicListElement key={topic}>
|
|
135
|
+
<>
|
|
136
|
+
{topic}
|
|
137
|
+
{i !== topics.length - 1 && <StyledTopicDivider>•</StyledTopicDivider>}
|
|
138
|
+
</>
|
|
139
|
+
</StyledTopicListElement>
|
|
140
|
+
))}
|
|
141
|
+
</StyledTopicList>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
@@ -36,6 +36,7 @@ const StyledHeading = styled.h1`
|
|
|
36
36
|
type Props = {
|
|
37
37
|
invertedStyle?: boolean;
|
|
38
38
|
toggleAdditionalResources: () => void;
|
|
39
|
+
onToggleAddToFavorites: (id: string | number, add: string) => void;
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
const ResourceGroup = ({
|
|
@@ -45,6 +46,7 @@ const ResourceGroup = ({
|
|
|
45
46
|
toggleAdditionalResources,
|
|
46
47
|
contentType,
|
|
47
48
|
invertedStyle,
|
|
49
|
+
onToggleAddToFavorites,
|
|
48
50
|
}: Props & ResourceListProps) => (
|
|
49
51
|
<Wrapper>
|
|
50
52
|
{title && (
|
|
@@ -59,6 +61,7 @@ const ResourceGroup = ({
|
|
|
59
61
|
showAdditionalResources={showAdditionalResources}
|
|
60
62
|
contentType={contentType}
|
|
61
63
|
resources={resources}
|
|
64
|
+
onToggleAddToFavorites={onToggleAddToFavorites}
|
|
62
65
|
/>
|
|
63
66
|
) : null}
|
|
64
67
|
</Wrapper>
|
|
@@ -15,6 +15,7 @@ import SafeLink from '@ndla/safelink';
|
|
|
15
15
|
import { Additional, Core, HumanMaleBoard } from '@ndla/icons/common';
|
|
16
16
|
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
17
17
|
import Tooltip from '@ndla/tooltip';
|
|
18
|
+
import { ArticleFavoritesButton } from '../Article';
|
|
18
19
|
import { Resource } from '../types';
|
|
19
20
|
import ContentTypeBadge from '../ContentTypeBadge';
|
|
20
21
|
import * as contentTypes from '../model/ContentType';
|
|
@@ -208,6 +209,7 @@ const IconWrapper = styled.div`
|
|
|
208
209
|
const TypeWrapper = styled.div`
|
|
209
210
|
display: flex;
|
|
210
211
|
align-items: center;
|
|
212
|
+
gap: ${spacing.xsmall};
|
|
211
213
|
`;
|
|
212
214
|
|
|
213
215
|
const ContentTypeName = styled.span`
|
|
@@ -219,15 +221,20 @@ const ContentTypeName = styled.span`
|
|
|
219
221
|
`;
|
|
220
222
|
|
|
221
223
|
type Props = {
|
|
224
|
+
id: string;
|
|
222
225
|
showContentTypeDescription?: boolean;
|
|
223
226
|
contentTypeName?: string;
|
|
224
227
|
contentTypeDescription?: string;
|
|
225
228
|
extraBottomMargin?: boolean;
|
|
226
229
|
showAdditionalResources?: boolean;
|
|
227
230
|
access?: 'teacher';
|
|
231
|
+
isFavorite?: boolean;
|
|
232
|
+
onToggleAddToFavorites: (id: string, add: boolean) => void;
|
|
233
|
+
hideAddToFavoriteButton?: boolean;
|
|
228
234
|
};
|
|
229
235
|
|
|
230
236
|
const ResourceItem = ({
|
|
237
|
+
id,
|
|
231
238
|
contentTypeName,
|
|
232
239
|
contentTypeDescription,
|
|
233
240
|
name,
|
|
@@ -238,6 +245,9 @@ const ResourceItem = ({
|
|
|
238
245
|
extraBottomMargin,
|
|
239
246
|
showAdditionalResources,
|
|
240
247
|
access,
|
|
248
|
+
onToggleAddToFavorites,
|
|
249
|
+
isFavorite,
|
|
250
|
+
hideAddToFavoriteButton,
|
|
241
251
|
}: Props & Resource) => {
|
|
242
252
|
const { t } = useTranslation();
|
|
243
253
|
const hidden = additional ? !showAdditionalResources : false;
|
|
@@ -291,6 +301,13 @@ const ResourceItem = ({
|
|
|
291
301
|
)}
|
|
292
302
|
</>
|
|
293
303
|
)}
|
|
304
|
+
{!hideAddToFavoriteButton && (
|
|
305
|
+
<ArticleFavoritesButton
|
|
306
|
+
isFavorite={isFavorite}
|
|
307
|
+
articleId={id}
|
|
308
|
+
onToggleAddToFavorites={() => onToggleAddToFavorites(id, true)}
|
|
309
|
+
/>
|
|
310
|
+
)}
|
|
294
311
|
</TypeWrapper>
|
|
295
312
|
</ListElement>
|
|
296
313
|
);
|
|
@@ -48,9 +48,19 @@ export type ResourceListProps = {
|
|
|
48
48
|
contentType?: string;
|
|
49
49
|
title?: string;
|
|
50
50
|
showAdditionalResources?: boolean;
|
|
51
|
+
onToggleAddToFavorites: (id: string, add: boolean) => void;
|
|
52
|
+
hideAddToFavoriteButton?: boolean;
|
|
51
53
|
};
|
|
52
54
|
|
|
53
|
-
const ResourceList = ({
|
|
55
|
+
const ResourceList = ({
|
|
56
|
+
resources,
|
|
57
|
+
onClick,
|
|
58
|
+
onToggleAddToFavorites,
|
|
59
|
+
contentType,
|
|
60
|
+
title,
|
|
61
|
+
showAdditionalResources,
|
|
62
|
+
hideAddToFavoriteButton,
|
|
63
|
+
}: ResourceListProps) => {
|
|
54
64
|
const { t } = useTranslation();
|
|
55
65
|
const renderAdditionalResourceTrigger =
|
|
56
66
|
!showAdditionalResources &&
|
|
@@ -60,11 +70,14 @@ const ResourceList = ({ resources, onClick, contentType, title, showAdditionalRe
|
|
|
60
70
|
return (
|
|
61
71
|
<div>
|
|
62
72
|
<StyledResourceList showAdditionalResources={showAdditionalResources}>
|
|
63
|
-
{resources.map((resource) => (
|
|
73
|
+
{resources.map(({ id, ...resource }) => (
|
|
64
74
|
<ResourceItem
|
|
65
|
-
|
|
75
|
+
id={id}
|
|
76
|
+
key={id}
|
|
66
77
|
contentType={contentType}
|
|
67
78
|
showAdditionalResources={showAdditionalResources}
|
|
79
|
+
hideAddToFavoriteButton={hideAddToFavoriteButton}
|
|
80
|
+
onToggleAddToFavorites={onToggleAddToFavorites}
|
|
68
81
|
{...resource}
|
|
69
82
|
contentTypeDescription={
|
|
70
83
|
resource.additional ? t('resource.tooltipAdditionalTopic') : t('resource.tooltipCoreTopic')
|