@ndla/ui 14.0.0 → 15.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/es/MyNdla/Resource/FolderInput.js +96 -0
- package/{lib/MyNdla/ResourceDash/ResourcesView.d.ts → es/MyNdla/Resource/index.js} +3 -3
- package/es/MyNdla/index.js +4 -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/FolderInput.d.ts +15 -0
- package/lib/MyNdla/Resource/FolderInput.js +116 -0
- package/lib/MyNdla/Resource/index.d.ts +10 -0
- package/lib/MyNdla/Resource/index.js +23 -0
- package/lib/MyNdla/index.d.ts +4 -4
- package/lib/MyNdla/index.js +13 -7
- 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 +75 -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 +12 -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 +145 -0
- package/src/MyNdla/Resource/FolderInput.tsx +104 -0
- package/src/MyNdla/Resource/index.ts +11 -0
- package/src/MyNdla/index.ts +4 -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,145 @@
|
|
|
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: ${spacing.small};
|
|
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
|
+
|
|
100
|
+
const IconCountWrapper = styled.div<IconCountWrapperProps>`
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
gap: 2px;
|
|
104
|
+
${fonts.sizes(16)};
|
|
105
|
+
${(p) =>
|
|
106
|
+
p.type === 'block' &&
|
|
107
|
+
css`
|
|
108
|
+
opacity: 0;
|
|
109
|
+
${FolderWrapper}:hover & {
|
|
110
|
+
opacity: 1;
|
|
111
|
+
}
|
|
112
|
+
`};
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const IconCount = ({ type, count, layoutType }: IconCountProps) => {
|
|
116
|
+
const Icon = type === 'resource' ? FileDocumentOutline : FolderOutlined;
|
|
117
|
+
const { t } = useTranslation();
|
|
118
|
+
if (!count) return null;
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<IconCountWrapper type={layoutType}>
|
|
122
|
+
<Icon aria-label={t(`myNdla.${type}s`)} />
|
|
123
|
+
<span>{layoutType === 'block' ? count : t(`myNdla.${type}s`, { count: 3 })}</span>
|
|
124
|
+
</IconCountWrapper>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
type LayoutType = 'list' | 'block';
|
|
129
|
+
|
|
130
|
+
const Folder = ({ link, title, subFolders, subResources, type = 'list', actionMenu }: Props) => {
|
|
131
|
+
const { t } = useTranslation();
|
|
132
|
+
return (
|
|
133
|
+
<FolderWrapper to={link}>
|
|
134
|
+
<FolderIconWrapper type={type}>
|
|
135
|
+
<FolderOutlined aria-label={t('myNdla.folder')} />
|
|
136
|
+
</FolderIconWrapper>
|
|
137
|
+
<FolderTitle>{title}</FolderTitle>
|
|
138
|
+
<IconCount layoutType={type} type={'folder'} count={subFolders} />
|
|
139
|
+
<IconCount layoutType={type} type={'resource'} count={subResources} />
|
|
140
|
+
<MenuButton size="small" />
|
|
141
|
+
</FolderWrapper>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export default Folder;
|
|
@@ -0,0 +1,104 @@
|
|
|
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 { IconButton } from '@ndla/button/src';
|
|
11
|
+
import { FolderOutlined } from '@ndla/icons/contentType';
|
|
12
|
+
import { Cross } from '@ndla/icons/action';
|
|
13
|
+
import React, { ChangeEvent, KeyboardEvent, useEffect, useRef, useState } from 'react';
|
|
14
|
+
import { useTranslation } from 'react-i18next';
|
|
15
|
+
import { colors, spacing } from '@ndla/core';
|
|
16
|
+
|
|
17
|
+
// Source: https://kovart.github.io/dashed-border-generator/
|
|
18
|
+
const borderStyle = `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(
|
|
19
|
+
colors.brand.tertiary,
|
|
20
|
+
)}' stroke-width='2' stroke-dasharray='8%2c8' stroke-dashoffset='4' stroke-linecap='square'/%3e%3c/svg%3e")`;
|
|
21
|
+
|
|
22
|
+
const FolderInputWrapper = styled.div`
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: row;
|
|
25
|
+
align-items: center;
|
|
26
|
+
padding: ${spacing.small};
|
|
27
|
+
|
|
28
|
+
background-image: ${borderStyle};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const StyledFolderIcon = styled.span`
|
|
32
|
+
display: flex;
|
|
33
|
+
padding: ${spacing.small};
|
|
34
|
+
svg {
|
|
35
|
+
color: ${colors.brand.primary};
|
|
36
|
+
height: 20px;
|
|
37
|
+
width: 20px;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const StyledInput = styled.input`
|
|
42
|
+
color: ${colors.brand.primary};
|
|
43
|
+
outline: none;
|
|
44
|
+
border: none;
|
|
45
|
+
margin-right: auto;
|
|
46
|
+
line-height: 1.75em;
|
|
47
|
+
|
|
48
|
+
::selection {
|
|
49
|
+
background: ${colors.brand.lighter};
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
interface Props {
|
|
54
|
+
onAddFolder: (name: string) => void;
|
|
55
|
+
onClose: () => void;
|
|
56
|
+
autoFocus?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const FolderInput = ({ onAddFolder, onClose, autoFocus }: Props) => {
|
|
60
|
+
const { t } = useTranslation();
|
|
61
|
+
const newFolderText = t('treeStructure.newFolder.defaultName');
|
|
62
|
+
const [input, setInput] = useState<string>(newFolderText);
|
|
63
|
+
const [mounted, setMounted] = useState(false);
|
|
64
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
65
|
+
|
|
66
|
+
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
67
|
+
setInput(e.target.value);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const onKeydown = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
71
|
+
if (e.key === 'Enter' && input) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
onAddFolder(input);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (mounted && autoFocus) {
|
|
79
|
+
inputRef.current?.select();
|
|
80
|
+
} else {
|
|
81
|
+
setMounted(true);
|
|
82
|
+
}
|
|
83
|
+
}, [mounted, autoFocus]);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<FolderInputWrapper>
|
|
87
|
+
<StyledFolderIcon>
|
|
88
|
+
<FolderOutlined />
|
|
89
|
+
</StyledFolderIcon>
|
|
90
|
+
<StyledInput
|
|
91
|
+
ref={inputRef}
|
|
92
|
+
value={input}
|
|
93
|
+
onChange={handleInputChange}
|
|
94
|
+
onKeyDown={onKeydown}
|
|
95
|
+
aria-label={newFolderText}
|
|
96
|
+
/>
|
|
97
|
+
<IconButton aria-label={t('close')} size="small" ghostPill onClick={onClose}>
|
|
98
|
+
<Cross />
|
|
99
|
+
</IconButton>
|
|
100
|
+
</FolderInputWrapper>
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default FolderInput;
|
|
@@ -0,0 +1,11 @@
|
|
|
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 Folder from './Folder';
|
|
10
|
+
import FolderInput from './FolderInput';
|
|
11
|
+
export { Folder, FolderInput };
|
package/src/MyNdla/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
export { ResourceElement, ResourcesView, Breadcrumbs };
|
|
1
|
+
import Folder from './Resource/Folder';
|
|
2
|
+
import FolderInput from './Resource/FolderInput';
|
|
3
|
+
import { VerticalNavigation } from './Navigation';
|
|
4
|
+
export { VerticalNavigation, Folder, FolderInput };
|
|
@@ -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>
|