@ndla/ui 25.3.0 → 26.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/LanguageSelector/LanguageSelector.js +12 -7
- package/es/TreeStructure/FolderItem.js +110 -94
- package/es/TreeStructure/FolderItems.js +26 -30
- package/es/TreeStructure/FolderNameInput.js +35 -27
- package/es/TreeStructure/NavigationLink.js +81 -0
- package/es/TreeStructure/TreeStructure.js +169 -45
- package/es/locale/messages-en.js +2 -0
- package/es/locale/messages-nb.js +2 -0
- package/es/locale/messages-nn.js +2 -0
- package/es/locale/messages-se.js +2 -0
- package/es/locale/messages-sma.js +2 -0
- package/lib/LanguageSelector/LanguageSelector.js +13 -7
- package/lib/TreeStructure/FolderItem.d.ts +2 -3
- package/lib/TreeStructure/FolderItem.js +107 -92
- package/lib/TreeStructure/FolderItems.d.ts +1 -3
- package/lib/TreeStructure/FolderItems.js +26 -29
- package/lib/TreeStructure/FolderNameInput.d.ts +2 -1
- package/lib/TreeStructure/FolderNameInput.js +33 -26
- package/lib/TreeStructure/NavigationLink.d.ts +15 -0
- package/lib/TreeStructure/NavigationLink.js +100 -0
- package/lib/TreeStructure/TreeStructure.d.ts +1 -2
- package/lib/TreeStructure/TreeStructure.js +163 -45
- package/lib/TreeStructure/types.d.ts +4 -1
- package/lib/locale/messages-en.d.ts +2 -0
- package/lib/locale/messages-en.js +2 -0
- package/lib/locale/messages-nb.d.ts +2 -0
- package/lib/locale/messages-nb.js +2 -0
- package/lib/locale/messages-nn.d.ts +2 -0
- package/lib/locale/messages-nn.js +2 -0
- package/lib/locale/messages-se.d.ts +2 -0
- package/lib/locale/messages-se.js +2 -0
- package/lib/locale/messages-sma.d.ts +2 -0
- package/lib/locale/messages-sma.js +2 -0
- package/package.json +11 -11
- package/src/LanguageSelector/LanguageSelector.tsx +4 -1
- package/src/TreeStructure/FolderItem.tsx +126 -104
- package/src/TreeStructure/FolderItems.tsx +51 -43
- package/src/TreeStructure/FolderNameInput.tsx +43 -28
- package/src/TreeStructure/NavigationLink.tsx +100 -0
- package/src/TreeStructure/TreeStructure.tsx +187 -61
- package/src/TreeStructure/types.ts +5 -1
- package/src/locale/messages-en.ts +2 -0
- package/src/locale/messages-nb.ts +2 -0
- package/src/locale/messages-nn.ts +2 -0
- package/src/locale/messages-se.ts +2 -0
- package/src/locale/messages-sma.ts +2 -0
- package/es/TreeStructure/TreeStructureWrapper.js +0 -13
- package/lib/TreeStructure/TreeStructureWrapper.d.ts +0 -12
- package/lib/TreeStructure/TreeStructureWrapper.js +0 -24
- package/src/TreeStructure/TreeStructureWrapper.tsx +0 -31
|
@@ -10,7 +10,9 @@ import { IFolder, IResource } from '@ndla/types-learningpath-api';
|
|
|
10
10
|
import { MenuItemProps } from '@ndla/button';
|
|
11
11
|
export interface FolderType extends IFolder {
|
|
12
12
|
icon?: ReactNode;
|
|
13
|
+
isNavigation?: boolean;
|
|
13
14
|
}
|
|
15
|
+
export declare type TreeStructureType = 'normal' | 'navigation' | 'picker';
|
|
14
16
|
export interface TreeStructureMenuProps extends Omit<MenuItemProps, 'onClick'> {
|
|
15
17
|
onClick: (e: MouseEvent<HTMLDivElement> | undefined, folder: FolderType) => void;
|
|
16
18
|
}
|
|
@@ -18,13 +20,14 @@ export interface CommonTreeStructureProps {
|
|
|
18
20
|
loading?: boolean;
|
|
19
21
|
onSelectFolder?: (id: string) => void;
|
|
20
22
|
openOnFolderClick?: boolean;
|
|
21
|
-
menuItems?: TreeStructureMenuProps[];
|
|
22
23
|
targetResource?: IResource;
|
|
23
24
|
framed?: boolean;
|
|
25
|
+
type: TreeStructureType;
|
|
24
26
|
}
|
|
25
27
|
export interface CommonFolderItemsProps extends CommonTreeStructureProps {
|
|
26
28
|
focusedFolderId?: string;
|
|
27
29
|
level: number;
|
|
30
|
+
maxLevel: number;
|
|
28
31
|
selectedFolder?: FolderType;
|
|
29
32
|
onCloseFolder: (id: string) => void;
|
|
30
33
|
onOpenFolder: (id: string) => void;
|
|
@@ -33,6 +33,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
33
33
|
edit: 'Edit foldername',
|
|
34
34
|
"delete": 'Delete'
|
|
35
35
|
},
|
|
36
|
+
hideFolders: 'Hide all folders',
|
|
37
|
+
showFolders: 'Show all folders',
|
|
36
38
|
createFolder: 'Create folder',
|
|
37
39
|
maxFoldersAlreadyAdded: 'Maximum subfolders reached',
|
|
38
40
|
newFolder: {
|
|
@@ -33,6 +33,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
33
33
|
edit: 'Endre mappenavn',
|
|
34
34
|
"delete": 'Slett'
|
|
35
35
|
},
|
|
36
|
+
hideFolders: 'Skjul alle mapper',
|
|
37
|
+
showFolders: 'Vis alle mapper',
|
|
36
38
|
createFolder: 'Lag mappe',
|
|
37
39
|
maxFoldersAlreadyAdded: 'Maks nivå av undermapper nådd',
|
|
38
40
|
newFolder: {
|
|
@@ -33,6 +33,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
33
33
|
edit: 'Endre mappenamn',
|
|
34
34
|
"delete": 'Slett'
|
|
35
35
|
},
|
|
36
|
+
hideFolders: 'Skjul alle mapper',
|
|
37
|
+
showFolders: 'Vis alle mapper',
|
|
36
38
|
createFolder: 'Lag mappe',
|
|
37
39
|
maxFoldersAlreadyAdded: 'Maks nivå av undermapper nådd',
|
|
38
40
|
newFolder: {
|
|
@@ -33,6 +33,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
33
33
|
edit: 'Rievdat máhpa nama',
|
|
34
34
|
"delete": 'Sihko'
|
|
35
35
|
},
|
|
36
|
+
hideFolders: 'Skjul alle mapper',
|
|
37
|
+
showFolders: 'Vis alle mapper',
|
|
36
38
|
createFolder: 'Ráhkat máhpa',
|
|
37
39
|
maxFoldersAlreadyAdded: 'Vuollemáhpaid badjerádji lea olahuvvon',
|
|
38
40
|
newFolder: {
|
|
@@ -33,6 +33,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
33
33
|
edit: 'Endre mappenavn',
|
|
34
34
|
"delete": 'Slett'
|
|
35
35
|
},
|
|
36
|
+
hideFolders: 'Skjul alle mapper',
|
|
37
|
+
showFolders: 'Vis alle mapper',
|
|
36
38
|
createFolder: 'Lag mappe',
|
|
37
39
|
maxFoldersAlreadyAdded: 'Maks nivå av undermapper nådd',
|
|
38
40
|
newFolder: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndla/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "26.0.0",
|
|
4
4
|
"description": "UI component library for NDLA.",
|
|
5
5
|
"license": "GPL-3.0",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -32,18 +32,18 @@
|
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@ndla/article-scripts": "^3.0.2",
|
|
35
|
-
"@ndla/button": "^3.
|
|
36
|
-
"@ndla/carousel": "^1.2.
|
|
35
|
+
"@ndla/button": "^3.5.0",
|
|
36
|
+
"@ndla/carousel": "^1.2.22",
|
|
37
37
|
"@ndla/core": "^2.3.5",
|
|
38
|
-
"@ndla/forms": "^3.2.
|
|
38
|
+
"@ndla/forms": "^3.2.1",
|
|
39
39
|
"@ndla/hooks": "^1.1.6",
|
|
40
|
-
"@ndla/icons": "^1.12.
|
|
41
|
-
"@ndla/licenses": "^5.0.
|
|
42
|
-
"@ndla/modal": "^1.3.
|
|
43
|
-
"@ndla/notion": "^3.1.
|
|
44
|
-
"@ndla/safelink": "^2.2.
|
|
40
|
+
"@ndla/icons": "^1.12.1",
|
|
41
|
+
"@ndla/licenses": "^5.0.16",
|
|
42
|
+
"@ndla/modal": "^1.3.6",
|
|
43
|
+
"@ndla/notion": "^3.1.40",
|
|
44
|
+
"@ndla/safelink": "^2.2.14",
|
|
45
45
|
"@ndla/switch": "^0.1.13",
|
|
46
|
-
"@ndla/tabs": "^1.1.
|
|
46
|
+
"@ndla/tabs": "^1.1.21",
|
|
47
47
|
"@ndla/tooltip": "^2.2.1",
|
|
48
48
|
"@ndla/types-learningpath-api": "^0.0.13",
|
|
49
49
|
"@ndla/util": "^3.1.0",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"publishConfig": {
|
|
86
86
|
"access": "public"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "e2029bce0a42c485f247a60ee895b2320fddc6ba"
|
|
89
89
|
}
|
|
@@ -115,7 +115,9 @@ interface StyledButtonProps {
|
|
|
115
115
|
inverted?: boolean;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const
|
|
118
|
+
const shouldForwardProp = (name: string) => name !== 'outline';
|
|
119
|
+
|
|
120
|
+
const StyledButton = styled(Button, { shouldForwardProp })<StyledButtonProps>`
|
|
119
121
|
border-color: ${({ inverted, outline }) =>
|
|
120
122
|
outline ? (inverted ? colors.white : colors.brand.primary) : 'transparent'};
|
|
121
123
|
`;
|
|
@@ -139,6 +141,7 @@ const LanguageSelector = ({ currentLanguage, outline, center, inverted, alwaysVi
|
|
|
139
141
|
</StyledSpan>
|
|
140
142
|
<ChevronDown />
|
|
141
143
|
</StyledButton>
|
|
144
|
+
|
|
142
145
|
{isOpen && (
|
|
143
146
|
<FocusTrapReact
|
|
144
147
|
active
|
|
@@ -6,40 +6,38 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { KeyboardEvent,
|
|
9
|
+
import React, { KeyboardEvent, useEffect, useRef } from 'react';
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import styled from '@emotion/styled';
|
|
12
|
-
import {
|
|
12
|
+
import { ArrowDropDownRounded } from '@ndla/icons/common';
|
|
13
13
|
import { Done } from '@ndla/icons/editor';
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import { colors, spacing, misc, animations } from '@ndla/core';
|
|
14
|
+
import { ButtonV2 as Button } from '@ndla/button';
|
|
15
|
+
import { colors, spacing, animations, spacingUnit, misc, fonts } from '@ndla/core';
|
|
17
16
|
import SafeLink from '@ndla/safelink';
|
|
18
17
|
import { CommonFolderItemsProps, FolderType } from './types';
|
|
19
18
|
import { arrowNavigation } from './arrowNavigation';
|
|
20
19
|
|
|
21
|
-
const OpenButton = styled.
|
|
22
|
-
background: transparent;
|
|
23
|
-
border: 0;
|
|
24
|
-
transform: rotate(${({ isOpen }) => (isOpen ? '0' : '-90')}deg);
|
|
25
|
-
padding: ${spacing.xsmall};
|
|
20
|
+
const OpenButton = styled.span<{ isOpen: boolean }>`
|
|
26
21
|
display: flex;
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
align-self: stretch;
|
|
25
|
+
color: ${colors.brand.tertiary};
|
|
26
|
+
${misc.transition.default};
|
|
29
27
|
cursor: pointer;
|
|
30
28
|
&:hover {
|
|
31
29
|
color: ${colors.brand.primary};
|
|
32
30
|
}
|
|
33
31
|
svg {
|
|
34
|
-
width:
|
|
35
|
-
height:
|
|
36
|
-
transform: ${({ isOpen }) => (isOpen ? '
|
|
32
|
+
width: 24px;
|
|
33
|
+
height: 24px;
|
|
34
|
+
transform: rotate(${({ isOpen }) => (isOpen ? '0' : '-90')}deg);
|
|
37
35
|
}
|
|
38
36
|
`;
|
|
39
37
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
align
|
|
38
|
+
const StyledName = styled.span`
|
|
39
|
+
grid-column-start: 2;
|
|
40
|
+
text-align: left;
|
|
43
41
|
`;
|
|
44
42
|
|
|
45
43
|
const WrapperForFolderChild = styled.div`
|
|
@@ -47,44 +45,39 @@ const WrapperForFolderChild = styled.div`
|
|
|
47
45
|
flex-direction: row;
|
|
48
46
|
align-items: center;
|
|
49
47
|
gap: ${spacing.xsmall};
|
|
48
|
+
margin-left: auto;
|
|
50
49
|
`;
|
|
51
50
|
|
|
52
|
-
const shouldForwardProp = (name: string) => !['selected', 'noArrow', 'fullWidth'].includes(name);
|
|
51
|
+
const shouldForwardProp = (name: string) => !['selected', 'noArrow', 'fullWidth', 'level'].includes(name);
|
|
53
52
|
|
|
54
53
|
interface FolderNameProps {
|
|
55
54
|
selected?: boolean;
|
|
56
55
|
noArrow?: boolean;
|
|
57
56
|
fullWidth?: boolean;
|
|
57
|
+
level: number;
|
|
58
|
+
isCreatingFolder?: boolean;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
const FolderName = styled(
|
|
61
|
-
cursor: pointer;
|
|
62
|
-
padding: ${spacing.xsmall};
|
|
63
|
-
margin: 0;
|
|
64
|
-
outline-offset: -2px;
|
|
65
|
-
outline-color: ${colors.brand.primary};
|
|
66
|
-
margin-left: ${({ noArrow }) => (noArrow ? `29px` : `0px`)};
|
|
67
|
-
flex-grow: ${({ fullWidth }) => fullWidth && 1};
|
|
61
|
+
const FolderName = styled(Button, { shouldForwardProp })<FolderNameProps>`
|
|
68
62
|
display: grid;
|
|
69
|
-
grid-template-columns:
|
|
70
|
-
|
|
63
|
+
grid-template-columns: ${spacing.medium} 1fr auto;
|
|
64
|
+
|
|
65
|
+
padding-left: ${({ level }) => 0.75 * spacingUnit * level}px;
|
|
71
66
|
gap: ${spacing.xxsmall};
|
|
72
|
-
border:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
border: none;
|
|
68
|
+
outline: none;
|
|
69
|
+
background: ${({ selected, isCreatingFolder }) => selected && !isCreatingFolder && colors.brand.lighter};
|
|
70
|
+
color: ${({ isCreatingFolder, selected }) =>
|
|
71
|
+
isCreatingFolder && selected ? colors.brand.primary : colors.text.primary};
|
|
77
72
|
transition: ${animations.durations.superFast};
|
|
78
|
-
text-align: left;
|
|
79
73
|
line-height: 1;
|
|
80
74
|
word-break: break-word;
|
|
81
75
|
&:hover,
|
|
82
76
|
&:focus {
|
|
77
|
+
box-shadow: none;
|
|
78
|
+
outline: none;
|
|
83
79
|
background: ${({ selected }) => (selected ? colors.brand.light : colors.brand.lightest)};
|
|
84
|
-
color: ${colors.
|
|
85
|
-
+ ${WrapperForFolderChild} {
|
|
86
|
-
opacity: 1;
|
|
87
|
-
}
|
|
80
|
+
color: ${colors.text.primary};
|
|
88
81
|
}
|
|
89
82
|
`;
|
|
90
83
|
|
|
@@ -92,25 +85,42 @@ const StyledDone = styled(Done)`
|
|
|
92
85
|
color: ${colors.support.green};
|
|
93
86
|
`;
|
|
94
87
|
|
|
95
|
-
const FolderNameLink =
|
|
88
|
+
const FolderNameLink = styled(SafeLink, { shouldForwardProp })<FolderNameProps>`
|
|
89
|
+
display: grid;
|
|
90
|
+
align-items: center;
|
|
91
|
+
grid-template-columns: ${spacing.medium} 1fr auto;
|
|
92
|
+
padding: ${spacing.small} ${spacing.xxsmall};
|
|
93
|
+
margin-left: ${({ level }) => 0.75 * spacingUnit * level}px;
|
|
94
|
+
gap: ${spacing.xxsmall};
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
|
|
97
|
+
border: none;
|
|
98
|
+
box-shadow: none;
|
|
99
|
+
color: ${({ selected }) => (selected ? colors.brand.primary : colors.text.primary)};
|
|
100
|
+
font-weight: ${({ selected }) => selected && fonts.weight.semibold};
|
|
101
|
+
font-size: ${fonts.sizes('16px')};
|
|
102
|
+
transition: ${animations.durations.superFast};
|
|
103
|
+
line-height: 1;
|
|
104
|
+
word-break: break-word;
|
|
105
|
+
&:hover,
|
|
106
|
+
&:focus {
|
|
107
|
+
color: ${colors.brand.primary};
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
96
110
|
|
|
97
111
|
interface Props extends CommonFolderItemsProps {
|
|
98
|
-
hideArrow?: boolean;
|
|
99
112
|
isOpen: boolean;
|
|
100
113
|
folder: FolderType;
|
|
101
|
-
|
|
114
|
+
isCreatingFolder?: boolean;
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
const FolderItem = ({
|
|
105
118
|
focusedFolderId,
|
|
106
|
-
menuItems,
|
|
107
|
-
hideArrow,
|
|
108
119
|
folder,
|
|
109
120
|
isOpen,
|
|
110
121
|
level,
|
|
111
122
|
loading,
|
|
112
123
|
selectedFolder,
|
|
113
|
-
noPaddingWhenArrowIsHidden,
|
|
114
124
|
onCloseFolder,
|
|
115
125
|
onOpenFolder,
|
|
116
126
|
onSelectFolder,
|
|
@@ -120,9 +130,11 @@ const FolderItem = ({
|
|
|
120
130
|
targetResource,
|
|
121
131
|
visibleFolders,
|
|
122
132
|
framed,
|
|
133
|
+
maxLevel,
|
|
134
|
+
isCreatingFolder,
|
|
123
135
|
}: Props) => {
|
|
124
136
|
const { t } = useTranslation();
|
|
125
|
-
const { id,
|
|
137
|
+
const { id, name } = folder;
|
|
126
138
|
const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
|
|
127
139
|
const selected = selectedFolder && selectedFolder.id === id;
|
|
128
140
|
const focused = focusedFolderId === id;
|
|
@@ -145,79 +157,89 @@ const FolderItem = ({
|
|
|
145
157
|
};
|
|
146
158
|
|
|
147
159
|
useEffect(() => {
|
|
148
|
-
if (focusedFolderId === id) {
|
|
160
|
+
if (focusedFolderId === id && !isCreatingFolder) {
|
|
149
161
|
ref.current?.focus();
|
|
150
162
|
}
|
|
151
|
-
}, [focusedFolderId, ref, id]);
|
|
152
|
-
|
|
153
|
-
const actions = menuItems?.map((item) => {
|
|
154
|
-
const { onClick } = item;
|
|
155
|
-
return {
|
|
156
|
-
...item,
|
|
157
|
-
onClick: (e?: MouseEvent<HTMLDivElement>) => onClick(e, folder),
|
|
158
|
-
};
|
|
159
|
-
});
|
|
163
|
+
}, [focusedFolderId, ref, id, isCreatingFolder]);
|
|
160
164
|
|
|
161
165
|
const linkPath = `/minndla${level > 0 ? '/folders' : ''}/${id}`;
|
|
162
166
|
|
|
163
167
|
const containsResource =
|
|
164
168
|
targetResource && folder.resources.some((resource) => resource.resourceId === targetResource.resourceId);
|
|
165
169
|
|
|
166
|
-
|
|
167
|
-
|
|
170
|
+
const emptyFolder = folder.subfolders.length === 0;
|
|
171
|
+
|
|
172
|
+
const isMaxDepth = level > maxLevel;
|
|
173
|
+
|
|
174
|
+
const hideArrow = isMaxDepth || emptyFolder;
|
|
175
|
+
|
|
176
|
+
return onSelectFolder ? (
|
|
177
|
+
<FolderName
|
|
178
|
+
variant="ghost"
|
|
179
|
+
shape="sharp"
|
|
180
|
+
fontWeight="normal"
|
|
181
|
+
colorTheme="light"
|
|
182
|
+
ref={ref}
|
|
183
|
+
level={level}
|
|
184
|
+
fullWidth={framed}
|
|
185
|
+
onKeyDown={(e) => arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)}
|
|
186
|
+
noArrow={hideArrow}
|
|
187
|
+
tabIndex={selected || focused ? 0 : -1}
|
|
188
|
+
selected={selected}
|
|
189
|
+
disabled={loading}
|
|
190
|
+
onFocus={() => setFocusedId(id)}
|
|
191
|
+
onClick={handleClickFolder}
|
|
192
|
+
isCreatingFolder={isCreatingFolder}>
|
|
168
193
|
{!hideArrow && (
|
|
169
194
|
<OpenButton
|
|
170
195
|
tabIndex={-1}
|
|
171
196
|
isOpen={isOpen}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
197
|
+
onClick={() => {
|
|
198
|
+
ref.current?.focus();
|
|
199
|
+
if (isOpen) {
|
|
200
|
+
onCloseFolder(id);
|
|
201
|
+
} else {
|
|
202
|
+
onOpenFolder(id);
|
|
203
|
+
}
|
|
204
|
+
}}>
|
|
205
|
+
<ArrowDropDownRounded />
|
|
175
206
|
</OpenButton>
|
|
176
207
|
)}
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
noArrow={hideArrow}
|
|
211
|
-
to={loading ? '' : linkPath}
|
|
212
|
-
tabIndex={selected || focused || level === 0 ? 0 : -1}
|
|
213
|
-
selected={selected}
|
|
214
|
-
onFocus={() => setFocusedId(id)}
|
|
215
|
-
onClick={handleClickFolder}>
|
|
216
|
-
{icon || <FolderOutlined />}
|
|
217
|
-
{name}
|
|
218
|
-
</FolderNameLink>
|
|
208
|
+
<StyledName>{name}</StyledName>
|
|
209
|
+
<WrapperForFolderChild>
|
|
210
|
+
{containsResource && <StyledDone title={t('myNdla.alreadyInFolder')} />}
|
|
211
|
+
</WrapperForFolderChild>
|
|
212
|
+
</FolderName>
|
|
213
|
+
) : (
|
|
214
|
+
<FolderNameLink
|
|
215
|
+
ref={ref}
|
|
216
|
+
level={level}
|
|
217
|
+
onKeyDown={(e: KeyboardEvent<HTMLElement>) =>
|
|
218
|
+
arrowNavigation(e, id, visibleFolders, setFocusedId, onOpenFolder, onCloseFolder)
|
|
219
|
+
}
|
|
220
|
+
noArrow={!isMaxDepth}
|
|
221
|
+
to={loading ? '' : linkPath}
|
|
222
|
+
tabIndex={selected || focused ? 0 : -1}
|
|
223
|
+
selected={selected}
|
|
224
|
+
onFocus={() => setFocusedId(id)}
|
|
225
|
+
onClick={handleClickFolder}>
|
|
226
|
+
{(!hideArrow || level === 0) && (
|
|
227
|
+
<OpenButton
|
|
228
|
+
tabIndex={-1}
|
|
229
|
+
isOpen={isOpen}
|
|
230
|
+
onClick={() => {
|
|
231
|
+
ref.current?.focus();
|
|
232
|
+
if (isOpen) {
|
|
233
|
+
onCloseFolder(id);
|
|
234
|
+
} else {
|
|
235
|
+
onOpenFolder(id);
|
|
236
|
+
}
|
|
237
|
+
}}>
|
|
238
|
+
<ArrowDropDownRounded />
|
|
239
|
+
</OpenButton>
|
|
219
240
|
)}
|
|
220
|
-
|
|
241
|
+
<StyledName>{name}</StyledName>
|
|
242
|
+
</FolderNameLink>
|
|
221
243
|
);
|
|
222
244
|
};
|
|
223
245
|
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
|
-
import { animations
|
|
11
|
+
import { animations } from '@ndla/core';
|
|
12
12
|
import FolderItem from './FolderItem';
|
|
13
13
|
import FolderNameInput from './FolderNameInput';
|
|
14
|
-
import { CommonFolderItemsProps, FolderType } from './types';
|
|
14
|
+
import { CommonFolderItemsProps, FolderType, TreeStructureType } from './types';
|
|
15
|
+
import NavigationLink from './NavigationLink';
|
|
15
16
|
|
|
16
|
-
const StyledUL = styled.ul
|
|
17
|
+
const StyledUL = styled.ul`
|
|
17
18
|
${animations.fadeInLeft(animations.durations.fast)};
|
|
18
19
|
animation-fill-mode: forwards;
|
|
19
20
|
@media (prefers-reduced-motion: reduce) {
|
|
@@ -22,18 +23,22 @@ const StyledUL = styled.ul<{ firstLevel?: boolean }>`
|
|
|
22
23
|
list-style: none;
|
|
23
24
|
margin: 0;
|
|
24
25
|
padding: 0;
|
|
25
|
-
margin-left: ${({ firstLevel }) => (firstLevel ? `-${spacing.xsmall}` : spacing.small)};
|
|
26
26
|
`;
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
interface StyledLiProps {
|
|
29
|
+
type?: TreeStructureType;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const StyledLI = styled.li<StyledLiProps>`
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
align-items: ${({ type }) => type === 'navigation' && 'flex-start'};
|
|
29
36
|
margin: 0;
|
|
30
37
|
padding: 0;
|
|
31
38
|
`;
|
|
32
39
|
|
|
33
40
|
export interface FolderItemsProps extends CommonFolderItemsProps {
|
|
34
41
|
folders: FolderType[];
|
|
35
|
-
editable?: boolean;
|
|
36
|
-
maximumLevelsOfFoldersAllowed: number;
|
|
37
42
|
newFolderParentId: string | undefined;
|
|
38
43
|
onCancelNewFolder: () => void;
|
|
39
44
|
onSaveNewFolder: (name: string, parentId: string) => void;
|
|
@@ -41,56 +46,59 @@ export interface FolderItemsProps extends CommonFolderItemsProps {
|
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
const FolderItems = ({
|
|
44
|
-
editable,
|
|
45
49
|
folders,
|
|
46
50
|
level,
|
|
47
51
|
loading,
|
|
48
|
-
maximumLevelsOfFoldersAllowed,
|
|
49
52
|
newFolderParentId,
|
|
50
53
|
onCancelNewFolder,
|
|
51
54
|
onSaveNewFolder,
|
|
52
55
|
openFolders,
|
|
56
|
+
type,
|
|
53
57
|
...rest
|
|
54
58
|
}: FolderItemsProps) => (
|
|
55
|
-
<StyledUL role=
|
|
59
|
+
<StyledUL role={level === 0 ? 'tree' : 'group'}>
|
|
56
60
|
{folders.map((folder) => {
|
|
57
61
|
const { subfolders, id } = folder;
|
|
58
62
|
const isOpen = openFolders?.includes(id);
|
|
59
63
|
|
|
60
64
|
return (
|
|
61
|
-
<StyledLI key={id} role="treeitem">
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
65
|
+
<StyledLI key={id} role="treeitem" type={type}>
|
|
66
|
+
{folder.isNavigation ? (
|
|
67
|
+
<NavigationLink folder={folder} isOpen={isOpen} level={level} type={type} loading={loading} {...rest} />
|
|
68
|
+
) : (
|
|
69
|
+
<>
|
|
70
|
+
<FolderItem
|
|
71
|
+
folder={folder}
|
|
72
|
+
isOpen={isOpen}
|
|
73
|
+
level={level}
|
|
74
|
+
loading={loading}
|
|
75
|
+
type={type}
|
|
76
|
+
isCreatingFolder={newFolderParentId === folder.id}
|
|
77
|
+
{...rest}
|
|
78
|
+
/>
|
|
79
|
+
{newFolderParentId === id && (
|
|
80
|
+
<FolderNameInput
|
|
81
|
+
loading={loading}
|
|
82
|
+
level={level}
|
|
83
|
+
onCancelNewFolder={onCancelNewFolder}
|
|
84
|
+
onSaveNewFolder={onSaveNewFolder}
|
|
85
|
+
parentId={newFolderParentId}
|
|
86
|
+
/>
|
|
87
|
+
)}
|
|
88
|
+
{subfolders && isOpen && (
|
|
89
|
+
<FolderItems
|
|
90
|
+
folders={subfolders}
|
|
91
|
+
level={level + 1}
|
|
92
|
+
loading={loading}
|
|
93
|
+
type={type}
|
|
94
|
+
newFolderParentId={newFolderParentId}
|
|
95
|
+
onCancelNewFolder={onCancelNewFolder}
|
|
96
|
+
onSaveNewFolder={onSaveNewFolder}
|
|
97
|
+
openFolders={openFolders}
|
|
98
|
+
{...rest}
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
</>
|
|
94
102
|
)}
|
|
95
103
|
</StyledLI>
|
|
96
104
|
);
|