@ndla/ui 20.0.2 → 22.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/LearningPaths/LearningPathInformation.js +21 -3
- package/es/LearningPaths/LearningPathMenu.js +8 -5
- package/es/LearningPaths/LearningPathMenuAsideCopyright.js +17 -4
- package/es/LearningPaths/LearningPathMenuIntro.js +19 -8
- package/es/MyNdla/Resource/Folder.js +7 -6
- package/es/Resource/BlockResource.js +6 -5
- package/es/Resource/ListResource.js +7 -6
- package/es/ResourceGroup/ResourceGroup.js +3 -3
- package/es/ResourceGroup/ResourceItem.js +12 -12
- package/es/ResourceGroup/ResourceList.js +2 -2
- package/es/TopicMenu/TopicMenuButton.js +4 -2
- package/es/TreeStructure/FolderItem.js +72 -51
- package/es/TreeStructure/FolderItems.js +33 -61
- package/es/TreeStructure/FolderNameInput.js +14 -13
- package/es/TreeStructure/TreeStructure.js +80 -96
- package/es/TreeStructure/helperFunctions.js +4 -73
- package/es/TreeStructure/{TreeStructure.types.js → types.js} +0 -0
- package/es/locale/messages-en.js +6 -1
- package/es/locale/messages-nb.js +6 -1
- package/es/locale/messages-nn.js +6 -1
- package/es/locale/messages-se.js +6 -1
- package/es/locale/messages-sma.js +6 -1
- package/lib/LearningPaths/LearningPathInformation.js +19 -2
- package/lib/LearningPaths/LearningPathMenu.d.ts +2 -1
- package/lib/LearningPaths/LearningPathMenu.js +8 -5
- package/lib/LearningPaths/LearningPathMenuAsideCopyright.js +16 -3
- package/lib/LearningPaths/LearningPathMenuIntro.d.ts +3 -1
- package/lib/LearningPaths/LearningPathMenuIntro.js +19 -8
- package/lib/MyNdla/Resource/Folder.js +7 -6
- package/lib/Resource/BlockResource.js +6 -5
- package/lib/Resource/ListResource.js +7 -6
- package/lib/ResourceGroup/ResourceGroup.d.ts +1 -1
- package/lib/ResourceGroup/ResourceGroup.js +3 -3
- package/lib/ResourceGroup/ResourceItem.d.ts +1 -1
- package/lib/ResourceGroup/ResourceItem.js +12 -12
- package/lib/ResourceGroup/ResourceList.d.ts +1 -1
- package/lib/ResourceGroup/ResourceList.js +2 -2
- package/lib/TopicMenu/TopicMenuButton.js +3 -1
- package/lib/TreeStructure/FolderItem.d.ts +6 -20
- package/lib/TreeStructure/FolderItem.js +74 -51
- package/lib/TreeStructure/FolderItems.d.ts +11 -2
- package/lib/TreeStructure/FolderItems.js +33 -61
- package/lib/TreeStructure/FolderNameInput.js +14 -13
- package/lib/TreeStructure/TreeStructure.d.ts +12 -2
- package/lib/TreeStructure/TreeStructure.js +78 -94
- package/lib/TreeStructure/helperFunctions.d.ts +2 -4
- package/lib/TreeStructure/helperFunctions.js +5 -80
- package/lib/TreeStructure/index.d.ts +2 -1
- package/lib/TreeStructure/types.d.ts +32 -0
- package/lib/TreeStructure/{TreeStructure.types.js → types.js} +0 -0
- package/lib/index.d.ts +1 -1
- package/lib/locale/messages-en.d.ts +6 -1
- package/lib/locale/messages-en.js +6 -1
- package/lib/locale/messages-nb.d.ts +6 -1
- package/lib/locale/messages-nb.js +6 -1
- package/lib/locale/messages-nn.d.ts +6 -1
- package/lib/locale/messages-nn.js +6 -1
- package/lib/locale/messages-se.d.ts +6 -1
- package/lib/locale/messages-se.js +6 -1
- package/lib/locale/messages-sma.d.ts +6 -1
- package/lib/locale/messages-sma.js +6 -1
- package/package.json +15 -14
- package/src/LearningPaths/LearningPathInformation.tsx +27 -12
- package/src/LearningPaths/LearningPathMenu.tsx +9 -1
- package/src/LearningPaths/LearningPathMenuAsideCopyright.tsx +22 -20
- package/src/LearningPaths/LearningPathMenuIntro.tsx +15 -2
- package/src/MyNdla/Resource/Folder.tsx +1 -1
- package/src/Resource/BlockResource.tsx +1 -1
- package/src/Resource/ListResource.tsx +1 -1
- package/src/ResourceGroup/ResourceGroup.tsx +1 -1
- package/src/ResourceGroup/ResourceItem.tsx +2 -2
- package/src/ResourceGroup/ResourceList.tsx +1 -1
- package/src/TopicMenu/TopicMenuButton.jsx +5 -1
- package/src/TreeStructure/FolderItem.tsx +59 -67
- package/src/TreeStructure/FolderItems.tsx +30 -50
- package/src/TreeStructure/FolderNameInput.tsx +6 -11
- package/src/TreeStructure/TreeStructure.tsx +73 -71
- package/src/TreeStructure/helperFunctions.ts +3 -37
- package/src/TreeStructure/index.ts +2 -1
- package/src/TreeStructure/types.ts +37 -0
- package/src/index.ts +1 -1
- package/src/locale/messages-en.ts +6 -1
- package/src/locale/messages-nb.ts +7 -1
- package/src/locale/messages-nn.ts +6 -1
- package/src/locale/messages-se.ts +7 -1
- package/src/locale/messages-sma.ts +7 -1
- package/lib/TreeStructure/TreeStructure.types.d.ts +0 -61
- package/src/TreeStructure/TreeStructure.types.ts +0 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "22.0.0",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -31,19 +31,20 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/button": "^3.
|
|
35
|
-
"@ndla/carousel": "^1.2.
|
|
36
|
-
"@ndla/core": "^2.3.
|
|
37
|
-
"@ndla/forms": "^3.1.
|
|
34
|
+
"@ndla/button": "^3.2.0",
|
|
35
|
+
"@ndla/carousel": "^1.2.15",
|
|
36
|
+
"@ndla/core": "^2.3.3",
|
|
37
|
+
"@ndla/forms": "^3.1.3",
|
|
38
38
|
"@ndla/hooks": "^1.1.4",
|
|
39
|
-
"@ndla/icons": "^1.11.
|
|
40
|
-
"@ndla/licenses": "^5.0.
|
|
41
|
-
"@ndla/modal": "^1.2.
|
|
42
|
-
"@ndla/notion": "^3.1.
|
|
43
|
-
"@ndla/safelink": "^2.2.
|
|
44
|
-
"@ndla/switch": "^0.1.
|
|
45
|
-
"@ndla/tabs": "^1.1.
|
|
46
|
-
"@ndla/tooltip": "^2.1.
|
|
39
|
+
"@ndla/icons": "^1.11.3",
|
|
40
|
+
"@ndla/licenses": "^5.0.6",
|
|
41
|
+
"@ndla/modal": "^1.2.16",
|
|
42
|
+
"@ndla/notion": "^3.1.26",
|
|
43
|
+
"@ndla/safelink": "^2.2.4",
|
|
44
|
+
"@ndla/switch": "^0.1.10",
|
|
45
|
+
"@ndla/tabs": "^1.1.14",
|
|
46
|
+
"@ndla/tooltip": "^2.1.5",
|
|
47
|
+
"@ndla/types-learningpath-api": "^0.0.12",
|
|
47
48
|
"@ndla/util": "^3.0.0",
|
|
48
49
|
"@reach/menu-button": "^0.16.2",
|
|
49
50
|
"@reach/slider": "^0.16.0",
|
|
@@ -82,5 +83,5 @@
|
|
|
82
83
|
"publishConfig": {
|
|
83
84
|
"access": "public"
|
|
84
85
|
},
|
|
85
|
-
"gitHead": "
|
|
86
|
+
"gitHead": "683202fb5ba5a555e6a3d61c89e5cce4ae3b151c"
|
|
86
87
|
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
|
-
import { fonts, spacing, spacingUnit, breakpoints, mq } from '@ndla/core';
|
|
11
|
+
import { fonts, spacing, spacingUnit, breakpoints, mq, colors } from '@ndla/core';
|
|
12
|
+
import { getLicenseByAbbreviation, LicenseByline } from '@ndla/licenses';
|
|
12
13
|
|
|
13
14
|
type StyledWrapperProps = {
|
|
14
15
|
invertedStyle?: boolean;
|
|
@@ -50,6 +51,17 @@ const StyledWrapper = styled.div<StyledWrapperProps>`
|
|
|
50
51
|
}
|
|
51
52
|
`;
|
|
52
53
|
|
|
54
|
+
const LicenseWrapper = styled.div`
|
|
55
|
+
ul {
|
|
56
|
+
margin-left: 0;
|
|
57
|
+
margin-bottom: ${spacing.small};
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const StyledHeader = styled.h1`
|
|
62
|
+
margin-bottom: ${spacing.small};
|
|
63
|
+
`;
|
|
64
|
+
|
|
53
65
|
interface Props {
|
|
54
66
|
description?: string;
|
|
55
67
|
title: string;
|
|
@@ -59,14 +71,17 @@ interface Props {
|
|
|
59
71
|
};
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
export const LearningPathInformation = ({ description, title, license, invertedStyle }: Props) =>
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
export const LearningPathInformation = ({ description, title, license, invertedStyle }: Props) => {
|
|
75
|
+
const { rights } = getLicenseByAbbreviation(license?.license || '', 'nb');
|
|
76
|
+
return (
|
|
77
|
+
<section className="o-wrapper">
|
|
78
|
+
<StyledWrapper invertedStyle={invertedStyle} className="c-article">
|
|
79
|
+
<LicenseWrapper>
|
|
80
|
+
<StyledHeader>{title}</StyledHeader>
|
|
81
|
+
<LicenseByline licenseRights={rights} color={colors.brand.tertiary} />
|
|
82
|
+
</LicenseWrapper>
|
|
83
|
+
{description && <div dangerouslySetInnerHTML={{ __html: description }} />}
|
|
84
|
+
</StyledWrapper>
|
|
85
|
+
</section>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
@@ -76,6 +76,7 @@ export type StepProps = {
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
interface Props {
|
|
79
|
+
onToggleAddToFavorites?: () => void;
|
|
79
80
|
learningsteps: StepProps[];
|
|
80
81
|
name: string;
|
|
81
82
|
lastUpdated: string;
|
|
@@ -109,6 +110,7 @@ const LearningPathMenu = ({
|
|
|
109
110
|
learningPathURL,
|
|
110
111
|
invertedStyle,
|
|
111
112
|
cookies,
|
|
113
|
+
onToggleAddToFavorites,
|
|
112
114
|
}: Props) => {
|
|
113
115
|
const { t } = useTranslation();
|
|
114
116
|
const [isOpen, toggleOpenState] = useState(false);
|
|
@@ -129,7 +131,13 @@ const LearningPathMenu = ({
|
|
|
129
131
|
</StyledToggleMenubutton>
|
|
130
132
|
</Tooltip>
|
|
131
133
|
</div>
|
|
132
|
-
<LearningPathMenuIntro
|
|
134
|
+
<LearningPathMenuIntro
|
|
135
|
+
isOpen={isOpen}
|
|
136
|
+
name={name}
|
|
137
|
+
invertedStyle={invertedStyle}
|
|
138
|
+
id={learningPathId}
|
|
139
|
+
onToggleAddToFavorites={onToggleAddToFavorites}
|
|
140
|
+
/>
|
|
133
141
|
<LearningPathMenuContent
|
|
134
142
|
learningsteps={learningsteps}
|
|
135
143
|
learningPathId={learningPathId}
|
|
@@ -9,22 +9,20 @@
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
11
|
import { User } from '@ndla/icons/common';
|
|
12
|
-
import { spacing, fonts } from '@ndla/core';
|
|
12
|
+
import { spacing, fonts, colors } from '@ndla/core';
|
|
13
|
+
import { getLicenseByAbbreviation, LicenseByline } from '@ndla/licenses';
|
|
13
14
|
|
|
14
15
|
const StyledLearningPathDetails = styled.div`
|
|
15
16
|
${fonts.sizes(14, 1.1)};
|
|
16
17
|
font-weight: ${fonts.weight.normal};
|
|
17
|
-
|
|
18
|
+
`;
|
|
19
|
+
|
|
20
|
+
const UserLine = styled.div`
|
|
18
21
|
display: flex;
|
|
19
|
-
align-items: flex-start;
|
|
20
|
-
justify-items: flex-start;
|
|
21
22
|
margin-bottom: ${spacing.xsmall};
|
|
22
|
-
|
|
23
|
-
margin: 0;
|
|
24
|
-
padding-left: ${spacing.xsmall};
|
|
25
|
-
}
|
|
23
|
+
|
|
26
24
|
span {
|
|
27
|
-
|
|
25
|
+
margin-left: ${spacing.xsmall};
|
|
28
26
|
}
|
|
29
27
|
`;
|
|
30
28
|
|
|
@@ -40,16 +38,20 @@ type Props = {
|
|
|
40
38
|
};
|
|
41
39
|
};
|
|
42
40
|
|
|
43
|
-
const LearningPathMenuAsideCopyright = ({ copyright }: Props) =>
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
const LearningPathMenuAsideCopyright = ({ copyright }: Props) => {
|
|
42
|
+
const { rights } = getLicenseByAbbreviation(copyright.license.license || '', 'nb');
|
|
43
|
+
return (
|
|
44
|
+
<StyledLearningPathDetails>
|
|
45
|
+
<UserLine>
|
|
46
|
+
<User />
|
|
47
|
+
{copyright.contributors.map((contributor) => (
|
|
48
|
+
<span key={contributor.name}>{contributor.name}</span>
|
|
49
|
+
))}
|
|
50
|
+
</UserLine>
|
|
51
|
+
|
|
52
|
+
<LicenseByline licenseRights={rights} color={colors.brand.tertiary} />
|
|
53
|
+
</StyledLearningPathDetails>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
54
56
|
|
|
55
57
|
export default LearningPathMenuAsideCopyright;
|
|
@@ -11,6 +11,7 @@ import styled from '@emotion/styled';
|
|
|
11
11
|
import { css } from '@emotion/core';
|
|
12
12
|
import { useTranslation } from 'react-i18next';
|
|
13
13
|
import { colors, spacing, spacingUnit, fonts, typography, mq, breakpoints, animations } from '@ndla/core';
|
|
14
|
+
import { ArticleFavoritesButton } from '../Article';
|
|
14
15
|
|
|
15
16
|
const StyledInfoHeader = styled.p`
|
|
16
17
|
${typography.smallHeading}
|
|
@@ -94,18 +95,30 @@ const StyledIntroHeader = styled.h1`
|
|
|
94
95
|
margin: ${spacing.small} 0 ${spacing.normal};
|
|
95
96
|
`;
|
|
96
97
|
|
|
98
|
+
const StyledRow = styled.div`
|
|
99
|
+
display: flex;
|
|
100
|
+
gap: ${spacing.small};
|
|
101
|
+
`;
|
|
102
|
+
|
|
97
103
|
interface Props {
|
|
98
104
|
isOpen: boolean;
|
|
99
105
|
invertedStyle?: boolean;
|
|
100
106
|
name: string;
|
|
107
|
+
onToggleAddToFavorites?: () => void;
|
|
108
|
+
id: number;
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
const LearningPathMenuIntro = ({ isOpen, name, invertedStyle }: Props) => {
|
|
111
|
+
const LearningPathMenuIntro = ({ isOpen, name, invertedStyle, onToggleAddToFavorites, id }: Props) => {
|
|
104
112
|
const { t } = useTranslation();
|
|
105
113
|
return (
|
|
106
114
|
<StyledMenuIntro isOpen={isOpen} invertedStyle={invertedStyle}>
|
|
107
115
|
<div>
|
|
108
|
-
<
|
|
116
|
+
<StyledRow>
|
|
117
|
+
<StyledInfoHeader>{t('learningPath.youAreInALearningPath')}</StyledInfoHeader>
|
|
118
|
+
{onToggleAddToFavorites && (
|
|
119
|
+
<ArticleFavoritesButton onToggleAddToFavorites={onToggleAddToFavorites} articleId={id.toString()} />
|
|
120
|
+
)}
|
|
121
|
+
</StyledRow>
|
|
109
122
|
<StyledIntroHeader>{name}</StyledIntroHeader>
|
|
110
123
|
</div>
|
|
111
124
|
</StyledMenuIntro>
|
|
@@ -141,7 +141,7 @@ const Folder = ({ link, title, subFolders, subResources, type = 'list', menuItem
|
|
|
141
141
|
<FolderTitle>{title}</FolderTitle>
|
|
142
142
|
<IconCount layoutType={type} type={'folder'} count={subFolders} />
|
|
143
143
|
<IconCount layoutType={type} type={'resource'} count={subResources} />
|
|
144
|
-
{menuItems && menuItems.length > 0 && <MenuButton size="small" menuItems={menuItems} />}
|
|
144
|
+
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
145
145
|
</FolderWrapper>
|
|
146
146
|
);
|
|
147
147
|
};
|
|
@@ -92,7 +92,7 @@ const BlockResource = ({ link, title, tags, resourceImage, topics, description,
|
|
|
92
92
|
<BlockDescription>{description}</BlockDescription>
|
|
93
93
|
<RightRow>
|
|
94
94
|
{tags && <CompressedTagList tags={tags} />}
|
|
95
|
-
{menuItems && menuItems.length > 0 && <MenuButton size="small" menuItems={menuItems} />}
|
|
95
|
+
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
96
96
|
</RightRow>
|
|
97
97
|
</BlockInfoWrapper>
|
|
98
98
|
</BlockElementWrapper>
|
|
@@ -131,7 +131,7 @@ const ListResource = ({ link, title, tags, resourceImage, topics, description, m
|
|
|
131
131
|
{showDescription && <ResourceDescription>{description}</ResourceDescription>}
|
|
132
132
|
<TagsandActionMenu>
|
|
133
133
|
{tags && <CompressedTagList tags={tags} />}
|
|
134
|
-
{menuItems && menuItems.length > 0 && <MenuButton size="small" menuItems={menuItems} />}
|
|
134
|
+
{menuItems && menuItems.length > 0 && <MenuButton alignRight size="small" menuItems={menuItems} />}
|
|
135
135
|
</TagsandActionMenu>
|
|
136
136
|
</ResourceWrapper>
|
|
137
137
|
);
|
|
@@ -36,7 +36,7 @@ const StyledHeading = styled.h1`
|
|
|
36
36
|
type Props = {
|
|
37
37
|
invertedStyle?: boolean;
|
|
38
38
|
toggleAdditionalResources: () => void;
|
|
39
|
-
onToggleAddToFavorites: (id: string
|
|
39
|
+
onToggleAddToFavorites: (id: string) => void;
|
|
40
40
|
showAddToFavoriteButton: boolean;
|
|
41
41
|
};
|
|
42
42
|
|
|
@@ -229,7 +229,7 @@ type Props = {
|
|
|
229
229
|
showAdditionalResources?: boolean;
|
|
230
230
|
access?: 'teacher';
|
|
231
231
|
isFavorite?: boolean;
|
|
232
|
-
onToggleAddToFavorites: (id: string
|
|
232
|
+
onToggleAddToFavorites: (id: string) => void;
|
|
233
233
|
showAddToFavoriteButton: boolean;
|
|
234
234
|
};
|
|
235
235
|
|
|
@@ -305,7 +305,7 @@ const ResourceItem = ({
|
|
|
305
305
|
<ArticleFavoritesButton
|
|
306
306
|
isFavorite={isFavorite}
|
|
307
307
|
articleId={id}
|
|
308
|
-
onToggleAddToFavorites={() => onToggleAddToFavorites(id
|
|
308
|
+
onToggleAddToFavorites={() => onToggleAddToFavorites(id)}
|
|
309
309
|
/>
|
|
310
310
|
)}
|
|
311
311
|
</TypeWrapper>
|
|
@@ -48,7 +48,7 @@ export type ResourceListProps = {
|
|
|
48
48
|
contentType?: string;
|
|
49
49
|
title?: string;
|
|
50
50
|
showAdditionalResources?: boolean;
|
|
51
|
-
onToggleAddToFavorites: (id: string
|
|
51
|
+
onToggleAddToFavorites: (id: string) => void;
|
|
52
52
|
showAddToFavoriteButton: boolean;
|
|
53
53
|
};
|
|
54
54
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import PropTypes from 'prop-types';
|
|
11
11
|
import { css } from '@emotion/core';
|
|
12
|
-
import { spacing, fonts, colors } from '@ndla/core';
|
|
12
|
+
import { spacing, fonts, colors, mq, breakpoints } from '@ndla/core';
|
|
13
13
|
import { Menu } from '@ndla/icons/common';
|
|
14
14
|
import Button from '@ndla/button';
|
|
15
15
|
|
|
@@ -38,6 +38,10 @@ const style = css`
|
|
|
38
38
|
background: ${colors.white};
|
|
39
39
|
color: ${colors.brand.primary};
|
|
40
40
|
}
|
|
41
|
+
${mq.range({ until: breakpoints.tablet })} {
|
|
42
|
+
padding-left: ${spacing.xsmall};
|
|
43
|
+
padding-right: ${spacing.xsmall};
|
|
44
|
+
}
|
|
41
45
|
`;
|
|
42
46
|
|
|
43
47
|
const TopicMenuButton = ({ ndlaFilm, children, ...rest }) => (
|
|
@@ -6,13 +6,14 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { KeyboardEvent, useEffect, useRef } from 'react';
|
|
9
|
+
import React, { KeyboardEvent, MouseEvent, useEffect, useRef } from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
11
|
import { ArrowDropDown } from '@ndla/icons/common';
|
|
12
|
+
import { MenuButton } from '@ndla/button';
|
|
12
13
|
import { FolderOutlined } from '@ndla/icons/contentType';
|
|
13
14
|
import { colors, spacing, misc, animations } from '@ndla/core';
|
|
14
15
|
import SafeLink from '@ndla/safelink';
|
|
15
|
-
import {
|
|
16
|
+
import { CommonFolderItemsProps, FolderType } from './types';
|
|
16
17
|
import { arrowNavigation } from './arrowNavigation';
|
|
17
18
|
|
|
18
19
|
const OpenButton = styled.button<{ isOpen: boolean }>`
|
|
@@ -39,10 +40,10 @@ const FolderItemWrapper = styled.div`
|
|
|
39
40
|
align-items: center;
|
|
40
41
|
`;
|
|
41
42
|
|
|
42
|
-
const WrapperForFolderChild = styled.div<{
|
|
43
|
+
const WrapperForFolderChild = styled.div<{ selected?: boolean }>`
|
|
43
44
|
position: absolute;
|
|
44
45
|
right: ${spacing.xsmall};
|
|
45
|
-
opacity: ${({
|
|
46
|
+
opacity: ${({ selected }) => (selected ? 1 : 0.25)};
|
|
46
47
|
&:hover,
|
|
47
48
|
&:focus,
|
|
48
49
|
&:focus-within {
|
|
@@ -50,16 +51,20 @@ const WrapperForFolderChild = styled.div<{ marked: boolean }>`
|
|
|
50
51
|
}
|
|
51
52
|
`;
|
|
52
53
|
|
|
53
|
-
const
|
|
54
|
-
|
|
54
|
+
const shouldForwardProp = (name: string) => !['selected', 'noArrow'].includes(name);
|
|
55
|
+
|
|
56
|
+
interface FolderNameProps {
|
|
57
|
+
selected?: boolean;
|
|
55
58
|
noArrow?: boolean;
|
|
56
|
-
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const FolderName = styled('button', { shouldForwardProp })<FolderNameProps>`
|
|
57
62
|
line-height: 1;
|
|
58
|
-
background: ${({
|
|
63
|
+
background: ${({ selected }) => (selected ? colors.brand.lighter : 'transparent')};
|
|
59
64
|
color: ${colors.text.primary};
|
|
60
65
|
&:hover,
|
|
61
66
|
&:focus {
|
|
62
|
-
background: ${({
|
|
67
|
+
background: ${({ selected }) => (selected ? colors.brand.light : colors.brand.lightest)};
|
|
63
68
|
color: ${colors.brand.primary};
|
|
64
69
|
+ ${WrapperForFolderChild} {
|
|
65
70
|
opacity: 1;
|
|
@@ -82,52 +87,40 @@ const FolderName = styled('button', { shouldForwardProp: (name) => !['marked', '
|
|
|
82
87
|
|
|
83
88
|
const FolderNameLink = FolderName.withComponent(SafeLink);
|
|
84
89
|
|
|
85
|
-
interface Props {
|
|
86
|
-
name: string;
|
|
87
|
-
id: string;
|
|
88
|
-
level: number;
|
|
89
|
-
onCloseFolder: (id: string) => void;
|
|
90
|
-
onOpenFolder: (id: string) => void;
|
|
91
|
-
onMarkFolder: (id: string) => void;
|
|
92
|
-
onSelectFolder?: (id: string) => void;
|
|
93
|
-
isOpen: boolean;
|
|
94
|
-
markedFolderId?: string;
|
|
95
|
-
focusedFolderId?: string;
|
|
96
|
-
visibleFolders: string[];
|
|
97
|
-
loading?: boolean;
|
|
98
|
-
openOnFolderClick?: boolean;
|
|
90
|
+
interface Props extends CommonFolderItemsProps {
|
|
99
91
|
hideArrow?: boolean;
|
|
100
|
-
|
|
101
|
-
|
|
92
|
+
isOpen: boolean;
|
|
93
|
+
folder: FolderType;
|
|
102
94
|
noPaddingWhenArrowIsHidden?: boolean;
|
|
103
|
-
folderChild?: FolderChildFuncType;
|
|
104
95
|
}
|
|
105
96
|
|
|
106
97
|
const FolderItem = ({
|
|
98
|
+
focusedFolderId,
|
|
99
|
+
menuItems,
|
|
107
100
|
hideArrow,
|
|
108
|
-
|
|
101
|
+
folder,
|
|
102
|
+
isOpen,
|
|
109
103
|
level,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
104
|
+
loading,
|
|
105
|
+
selectedFolder,
|
|
106
|
+
noPaddingWhenArrowIsHidden,
|
|
113
107
|
onCloseFolder,
|
|
114
108
|
onOpenFolder,
|
|
115
|
-
onMarkFolder,
|
|
116
109
|
onSelectFolder,
|
|
117
|
-
isOpen,
|
|
118
|
-
markedFolderId,
|
|
119
|
-
focusedFolderId,
|
|
120
110
|
openOnFolderClick,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
folderChild,
|
|
111
|
+
setFocusedId,
|
|
112
|
+
setSelectedFolder,
|
|
113
|
+
visibleFolders,
|
|
125
114
|
}: Props) => {
|
|
115
|
+
const { id, icon, name } = folder;
|
|
126
116
|
const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
|
|
127
|
-
const
|
|
117
|
+
const selected = selectedFolder && selectedFolder.id === id;
|
|
118
|
+
const focused = focusedFolderId === id;
|
|
128
119
|
|
|
129
|
-
const
|
|
130
|
-
|
|
120
|
+
const handleClickFolder = () => {
|
|
121
|
+
setSelectedFolder(folder);
|
|
122
|
+
setFocusedId(id);
|
|
123
|
+
onSelectFolder?.(id);
|
|
131
124
|
if (openOnFolderClick) {
|
|
132
125
|
if (isOpen) {
|
|
133
126
|
onCloseFolder(id);
|
|
@@ -139,12 +132,20 @@ const FolderItem = ({
|
|
|
139
132
|
|
|
140
133
|
useEffect(() => {
|
|
141
134
|
if (focusedFolderId === id) {
|
|
142
|
-
|
|
143
|
-
ref.current.focus();
|
|
144
|
-
}
|
|
135
|
+
ref.current?.focus();
|
|
145
136
|
}
|
|
146
137
|
}, [focusedFolderId, ref, id]);
|
|
147
138
|
|
|
139
|
+
const actions = menuItems?.map((item) => {
|
|
140
|
+
const { onClick } = item;
|
|
141
|
+
return {
|
|
142
|
+
...item,
|
|
143
|
+
onClick: (e?: MouseEvent<HTMLDivElement>) => onClick(e, folder),
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const linkPath = `/minndla${level > 1 ? '/folders' : ''}/${id}`;
|
|
148
|
+
|
|
148
149
|
return (
|
|
149
150
|
<FolderItemWrapper>
|
|
150
151
|
{!hideArrow && (
|
|
@@ -160,24 +161,19 @@ const FolderItem = ({
|
|
|
160
161
|
<>
|
|
161
162
|
<FolderName
|
|
162
163
|
ref={ref}
|
|
163
|
-
onKeyDown={(e) => arrowNavigation(e, id, visibleFolders,
|
|
164
|
+
onKeyDown={(e) => arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)}
|
|
164
165
|
noArrow={hideArrow && !noPaddingWhenArrowIsHidden}
|
|
165
|
-
tabIndex={
|
|
166
|
-
|
|
166
|
+
tabIndex={selected || focused ? 0 : -1}
|
|
167
|
+
selected={selected}
|
|
167
168
|
disabled={loading}
|
|
168
|
-
onFocus={() =>
|
|
169
|
-
|
|
170
|
-
}}
|
|
171
|
-
onClick={() => {
|
|
172
|
-
handleMarkFolder();
|
|
173
|
-
onSelectFolder(id);
|
|
174
|
-
}}>
|
|
169
|
+
onFocus={() => setFocusedId(id)}
|
|
170
|
+
onClick={handleClickFolder}>
|
|
175
171
|
{icon || <FolderOutlined />}
|
|
176
172
|
{name}
|
|
177
173
|
</FolderName>
|
|
178
|
-
{
|
|
179
|
-
<WrapperForFolderChild
|
|
180
|
-
{
|
|
174
|
+
{actions && (
|
|
175
|
+
<WrapperForFolderChild selected={selected}>
|
|
176
|
+
<MenuButton size="xsmall" menuItems={actions} tabIndex={selected || id === focusedFolderId ? 0 : -1} />
|
|
181
177
|
</WrapperForFolderChild>
|
|
182
178
|
)}
|
|
183
179
|
</>
|
|
@@ -185,18 +181,14 @@ const FolderItem = ({
|
|
|
185
181
|
<FolderNameLink
|
|
186
182
|
ref={ref}
|
|
187
183
|
onKeyDown={(e: KeyboardEvent<HTMLElement>) =>
|
|
188
|
-
arrowNavigation(e, id, visibleFolders,
|
|
184
|
+
arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)
|
|
189
185
|
}
|
|
190
186
|
noArrow={hideArrow}
|
|
191
|
-
to={loading ? '' :
|
|
192
|
-
tabIndex={
|
|
193
|
-
|
|
194
|
-
onFocus={() =>
|
|
195
|
-
|
|
196
|
-
}}
|
|
197
|
-
onClick={() => {
|
|
198
|
-
handleMarkFolder();
|
|
199
|
-
}}>
|
|
187
|
+
to={loading ? '' : linkPath}
|
|
188
|
+
tabIndex={selected || focused || level === 1 ? 0 : -1}
|
|
189
|
+
selected={selected}
|
|
190
|
+
onFocus={() => setFocusedId(id)}
|
|
191
|
+
onClick={handleClickFolder}>
|
|
200
192
|
{icon || <FolderOutlined />}
|
|
201
193
|
{name}
|
|
202
194
|
</FolderNameLink>
|
|
@@ -11,7 +11,7 @@ import styled from '@emotion/styled';
|
|
|
11
11
|
import { animations, spacing } from '@ndla/core';
|
|
12
12
|
import FolderItem from './FolderItem';
|
|
13
13
|
import FolderNameInput from './FolderNameInput';
|
|
14
|
-
import {
|
|
14
|
+
import { CommonFolderItemsProps, FolderType } from './types';
|
|
15
15
|
|
|
16
16
|
const StyledUL = styled.ul<{ firstLevel?: boolean }>`
|
|
17
17
|
${animations.fadeInLeft(animations.durations.fast)};
|
|
@@ -30,85 +30,65 @@ const StyledLI = styled.li`
|
|
|
30
30
|
padding: 0;
|
|
31
31
|
`;
|
|
32
32
|
|
|
33
|
+
export interface FolderItemsProps extends CommonFolderItemsProps {
|
|
34
|
+
folders: FolderType[];
|
|
35
|
+
editable?: boolean;
|
|
36
|
+
maximumLevelsOfFoldersAllowed: number;
|
|
37
|
+
newFolderParentId: string | undefined;
|
|
38
|
+
onCancelNewFolder: () => void;
|
|
39
|
+
onSaveNewFolder: (name: string, parentId: string) => void;
|
|
40
|
+
openFolders: string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
const FolderItems = ({
|
|
34
|
-
|
|
44
|
+
editable,
|
|
35
45
|
folders,
|
|
36
46
|
level,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
onOpenFolder,
|
|
41
|
-
onCreateNewFolder,
|
|
47
|
+
loading,
|
|
48
|
+
maximumLevelsOfFoldersAllowed,
|
|
49
|
+
newFolderParentId,
|
|
42
50
|
onCancelNewFolder,
|
|
43
51
|
onSaveNewFolder,
|
|
44
|
-
newFolderParentId,
|
|
45
|
-
visibleFolders,
|
|
46
52
|
openFolders,
|
|
47
|
-
|
|
48
|
-
onMarkFolder,
|
|
49
|
-
openOnFolderClick,
|
|
50
|
-
focusedFolderId,
|
|
51
|
-
setFocusedFolderId,
|
|
52
|
-
folderChild,
|
|
53
|
-
maximumLevelsOfFoldersAllowed,
|
|
53
|
+
...rest
|
|
54
54
|
}: FolderItemsProps) => (
|
|
55
55
|
<StyledUL role="group" firstLevel={level === 1}>
|
|
56
|
-
{folders.map((
|
|
56
|
+
{folders.map((folder) => {
|
|
57
|
+
const { subfolders, id } = folder;
|
|
57
58
|
const isOpen = openFolders?.includes(id);
|
|
58
59
|
return (
|
|
59
60
|
<StyledLI key={id} role="treeitem">
|
|
60
61
|
<div>
|
|
61
62
|
<FolderItem
|
|
63
|
+
hideArrow={subfolders?.length === 0 || level > maximumLevelsOfFoldersAllowed}
|
|
64
|
+
folder={folder}
|
|
65
|
+
isOpen={isOpen}
|
|
62
66
|
level={level}
|
|
63
|
-
icon={icon}
|
|
64
|
-
onSelectFolder={onSelectFolder}
|
|
65
|
-
openOnFolderClick={openOnFolderClick}
|
|
66
67
|
loading={loading}
|
|
67
|
-
isOpen={isOpen}
|
|
68
|
-
id={id}
|
|
69
|
-
visibleFolders={visibleFolders}
|
|
70
|
-
name={name}
|
|
71
|
-
markedFolderId={markedFolderId}
|
|
72
|
-
focusedFolderId={focusedFolderId}
|
|
73
|
-
onMarkFolder={onMarkFolder}
|
|
74
|
-
onCloseFolder={onCloseFolder}
|
|
75
|
-
onOpenFolder={onOpenFolder}
|
|
76
|
-
hideArrow={subfolders?.length === 0 || level > maximumLevelsOfFoldersAllowed}
|
|
77
68
|
noPaddingWhenArrowIsHidden={editable && level === 1 && subfolders?.length === 0}
|
|
78
|
-
|
|
79
|
-
folderChild={folderChild}
|
|
69
|
+
{...rest}
|
|
80
70
|
/>
|
|
81
71
|
</div>
|
|
82
72
|
{newFolderParentId === id && (
|
|
83
73
|
<FolderNameInput
|
|
84
|
-
parentId={newFolderParentId}
|
|
85
74
|
loading={loading}
|
|
86
75
|
onCancelNewFolder={onCancelNewFolder}
|
|
87
76
|
onSaveNewFolder={onSaveNewFolder}
|
|
77
|
+
parentId={newFolderParentId}
|
|
88
78
|
/>
|
|
89
79
|
)}
|
|
90
80
|
{subfolders && isOpen && (
|
|
91
81
|
<FolderItems
|
|
92
|
-
onSelectFolder={onSelectFolder}
|
|
93
|
-
loading={loading}
|
|
94
|
-
newFolderParentId={newFolderParentId}
|
|
95
|
-
visibleFolders={visibleFolders}
|
|
96
|
-
openFolders={openFolders}
|
|
97
|
-
level={level + 1}
|
|
98
82
|
editable={editable}
|
|
99
83
|
folders={subfolders}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
onCreateNewFolder={onCreateNewFolder}
|
|
103
|
-
onSaveNewFolder={onSaveNewFolder}
|
|
104
|
-
onCancelNewFolder={onCancelNewFolder}
|
|
105
|
-
markedFolderId={markedFolderId}
|
|
106
|
-
onMarkFolder={onMarkFolder}
|
|
107
|
-
openOnFolderClick={openOnFolderClick}
|
|
108
|
-
focusedFolderId={focusedFolderId}
|
|
109
|
-
setFocusedFolderId={setFocusedFolderId}
|
|
110
|
-
folderChild={folderChild}
|
|
84
|
+
level={level + 1}
|
|
85
|
+
loading={loading}
|
|
111
86
|
maximumLevelsOfFoldersAllowed={maximumLevelsOfFoldersAllowed}
|
|
87
|
+
newFolderParentId={newFolderParentId}
|
|
88
|
+
onCancelNewFolder={onCancelNewFolder}
|
|
89
|
+
onSaveNewFolder={onSaveNewFolder}
|
|
90
|
+
openFolders={openFolders}
|
|
91
|
+
{...rest}
|
|
112
92
|
/>
|
|
113
93
|
)}
|
|
114
94
|
</StyledLI>
|