@ndla/ui 25.2.1 → 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/Article/ArticleByline.js +17 -7
- package/es/Article/ArticleSideBar.js +5 -4
- package/es/Breadcrumb/BreadcrumbItem.js +8 -7
- package/es/ErrorMessage/ErrorMessage.js +12 -6
- package/es/Frontpage/FrontpageHeader.js +7 -9
- package/es/LanguageSelector/LanguageSelector.js +12 -7
- package/es/LearningPaths/LearningPathInformation.js +8 -5
- package/es/Subject/SubjectHeader.js +5 -6
- 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 +7 -22
- package/es/locale/messages-nb.js +8 -23
- package/es/locale/messages-nn.js +7 -22
- package/es/locale/messages-se.js +697 -712
- package/es/locale/messages-sma.js +8 -23
- package/lib/Article/ArticleByline.js +17 -7
- package/lib/Article/ArticleSideBar.js +5 -4
- package/lib/Breadcrumb/BreadcrumbItem.js +8 -7
- package/lib/ErrorMessage/ErrorMessage.d.ts +1 -0
- package/lib/ErrorMessage/ErrorMessage.js +12 -6
- package/lib/Frontpage/FrontpageHeader.d.ts +5 -6
- package/lib/Frontpage/FrontpageHeader.js +7 -11
- package/lib/LanguageSelector/LanguageSelector.js +13 -7
- package/lib/LearningPaths/LearningPathInformation.d.ts +2 -1
- package/lib/LearningPaths/LearningPathInformation.js +8 -5
- package/lib/Subject/SubjectHeader.js +14 -16
- 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 +4 -19
- package/lib/locale/messages-en.js +7 -22
- package/lib/locale/messages-nb.d.ts +4 -19
- package/lib/locale/messages-nb.js +8 -23
- package/lib/locale/messages-nn.d.ts +4 -19
- package/lib/locale/messages-nn.js +7 -22
- package/lib/locale/messages-se.d.ts +4 -19
- package/lib/locale/messages-se.js +697 -712
- package/lib/locale/messages-sma.d.ts +4 -19
- package/lib/locale/messages-sma.js +8 -23
- package/package.json +14 -14
- package/src/Article/ArticleByline.tsx +10 -3
- package/src/Article/ArticleSideBar.tsx +1 -0
- package/src/Breadcrumb/BreadcrumbItem.tsx +1 -1
- package/src/ErrorMessage/ErrorMessage.tsx +6 -0
- package/src/Frontpage/FrontpageHeader.tsx +5 -6
- package/src/LanguageSelector/LanguageSelector.tsx +4 -1
- package/src/LearningPaths/LearningPathInformation.tsx +3 -2
- package/src/Subject/SubjectHeader.tsx +1 -2
- 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 +9 -22
- package/src/locale/messages-nb.ts +10 -23
- package/src/locale/messages-nn.ts +9 -22
- package/src/locale/messages-se.ts +724 -738
- package/src/locale/messages-sma.ts +10 -23
- 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
|
@@ -92,6 +92,8 @@ declare const messages: {
|
|
|
92
92
|
myPage: string;
|
|
93
93
|
deleteAccount: string;
|
|
94
94
|
logout: string;
|
|
95
|
+
loginText: string;
|
|
96
|
+
loginTextLink: string;
|
|
95
97
|
loginTerms: string;
|
|
96
98
|
loginResourcePitch: string;
|
|
97
99
|
loginWelcome: string;
|
|
@@ -167,6 +169,8 @@ declare const messages: {
|
|
|
167
169
|
edit: string;
|
|
168
170
|
delete: string;
|
|
169
171
|
};
|
|
172
|
+
hideFolders: string;
|
|
173
|
+
showFolders: string;
|
|
170
174
|
createFolder: string;
|
|
171
175
|
maxFoldersAlreadyAdded: string;
|
|
172
176
|
newFolder: {
|
|
@@ -392,13 +396,6 @@ declare const messages: {
|
|
|
392
396
|
name: string;
|
|
393
397
|
};
|
|
394
398
|
};
|
|
395
|
-
category: {
|
|
396
|
-
fellesfag: string;
|
|
397
|
-
yrkesfag: string;
|
|
398
|
-
studiespesialiserende: string;
|
|
399
|
-
imported: string;
|
|
400
|
-
heading: string;
|
|
401
|
-
};
|
|
402
399
|
errorDescription: string;
|
|
403
400
|
film: {
|
|
404
401
|
header: string;
|
|
@@ -955,18 +952,6 @@ declare const messages: {
|
|
|
955
952
|
text: string;
|
|
956
953
|
};
|
|
957
954
|
};
|
|
958
|
-
fagfornyelse: {
|
|
959
|
-
frontpage: {
|
|
960
|
-
heading: string;
|
|
961
|
-
text: string;
|
|
962
|
-
blogHeading: string;
|
|
963
|
-
};
|
|
964
|
-
badge: {
|
|
965
|
-
heading: string;
|
|
966
|
-
text: string;
|
|
967
|
-
linkText: string;
|
|
968
|
-
};
|
|
969
|
-
};
|
|
970
955
|
frontPageToolbox: {
|
|
971
956
|
heading: string;
|
|
972
957
|
text: string;
|
|
@@ -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: {
|
|
@@ -248,13 +250,6 @@ var messages = _objectSpread(_objectSpread({
|
|
|
248
250
|
name: 'Følg oss'
|
|
249
251
|
}
|
|
250
252
|
},
|
|
251
|
-
category: {
|
|
252
|
-
fellesfag: 'Fellesfag',
|
|
253
|
-
yrkesfag: 'Yrkesfag',
|
|
254
|
-
studiespesialiserende: 'Studieforberedende',
|
|
255
|
-
imported: 'Spolte fag',
|
|
256
|
-
heading: 'Hva lærer du?'
|
|
257
|
-
},
|
|
258
253
|
errorDescription: 'Beklager, en feil oppstod under lasting av fagene.',
|
|
259
254
|
film: {
|
|
260
255
|
header: 'NDLA film',
|
|
@@ -787,18 +782,6 @@ var messages = _objectSpread(_objectSpread({
|
|
|
787
782
|
text: 'er utarbeidet av'
|
|
788
783
|
}
|
|
789
784
|
},
|
|
790
|
-
fagfornyelse: {
|
|
791
|
-
frontpage: {
|
|
792
|
-
heading: 'Velkommen til sniktitt på Fagfornyelsen i NDLA',
|
|
793
|
-
text: 'Høsten 2020 og 2021 vil de nye læreplanene tre i kraft. I NDLA har vi startet med dette arbeidet allerede. Våre innholdsansvarlige lager hver dag nye supre læringsressurser som er tilrettelagt for de nye planene. På denne siden kan du se dem allerede nå.',
|
|
794
|
-
blogHeading: 'Vil du vite mer?'
|
|
795
|
-
},
|
|
796
|
-
badge: {
|
|
797
|
-
heading: 'Denne siden er tilrettelagt for fagfornyelsen 2020/2021',
|
|
798
|
-
text: 'Innholdet er under arbeid. Ikke på jakt etter dette?',
|
|
799
|
-
linkText: 'Gå til ndla.no for dagens innhold'
|
|
800
|
-
}
|
|
801
|
-
},
|
|
802
785
|
frontPageToolbox: {
|
|
803
786
|
heading: 'Verktøykassa',
|
|
804
787
|
text: 'Har du lyst til å bli god til å presentere, eller vil du lære å studere smartere ved hjelp av riktig studieteknikk? Trenger du råd om hvordan du leser mest mulig effektivt til eksamen? I verktøykassa til NDLA finner du masse gode tips og råd!',
|
|
@@ -970,14 +953,14 @@ var messages = _objectSpread(_objectSpread({
|
|
|
970
953
|
folders_plural: '{{count}} mapper',
|
|
971
954
|
folder: {
|
|
972
955
|
folder: 'Mappe',
|
|
973
|
-
"delete": 'Slett',
|
|
974
|
-
edit: 'Rediger',
|
|
956
|
+
"delete": 'Slett mappe',
|
|
957
|
+
edit: 'Rediger mappe',
|
|
975
958
|
missingName: 'Skriv navn på mappe',
|
|
976
959
|
folderDeleted: '"{{folderName}}" er slettet'
|
|
977
960
|
},
|
|
978
961
|
tags: '{{count}} emneknagg',
|
|
979
962
|
tags_plural: '{{count}} emneknagger',
|
|
980
|
-
confirmDeleteFolder: 'Er du sikker på at du vil slette
|
|
963
|
+
confirmDeleteFolder: 'Er du sikker på at du vil slette mappa? Dersom mappa har undermappar vil disse også slettes. Denne handlinga kan ikkje endrast.',
|
|
981
964
|
confirmDeleteTag: 'Er du sikker på at du vil slette emneknagg? Denne handlingen kan ikke endres.',
|
|
982
965
|
myFolders: 'Mine mapper',
|
|
983
966
|
myTags: 'Mine emneknagger',
|
|
@@ -999,6 +982,8 @@ var messages = _objectSpread(_objectSpread({
|
|
|
999
982
|
myPage: 'Min side',
|
|
1000
983
|
deleteAccount: 'Slett Min NDLA',
|
|
1001
984
|
logout: 'Logg ut av Min NDLA',
|
|
985
|
+
loginText: 'Logg på med Feide for å få tilgang til Min NDLA. Vi ber om at du ikkje skriver noko støtende, personsensitiv informasjon eller andre persondata i tekstfelt. Les vår ',
|
|
986
|
+
loginTextLink: 'personvernerklæring her',
|
|
1002
987
|
loginTerms: 'Logg på med Feide for å få tilgang. Ved å logge på godkjenner du våre vilkår for bruk',
|
|
1003
988
|
loginResourcePitch: 'Ønsker du å favorittmerke denne siden?',
|
|
1004
989
|
loginWelcome: 'Velkommen til NDLA! Her kan du organisere fagstoffet på <i>din</i> måte!',
|
|
@@ -1043,7 +1028,7 @@ var messages = _objectSpread(_objectSpread({
|
|
|
1043
1028
|
linkCopied: 'Kopiert til utklippstavle',
|
|
1044
1029
|
addToMyNdla: 'Legg i Min NDLA',
|
|
1045
1030
|
addedToMyNdla: 'Lagt i Min NDLA',
|
|
1046
|
-
addedToFolder: 'Ressurs er lagt i
|
|
1031
|
+
addedToFolder: 'Ressurs er lagt i ',
|
|
1047
1032
|
removedFromFolder: 'Fjernet fra "{{folderName}}"',
|
|
1048
1033
|
titleUpdated: 'Tittel oppdatert',
|
|
1049
1034
|
tagsUpdated: 'Emneknagger oppdatert',
|
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",
|
|
@@ -31,22 +31,22 @@
|
|
|
31
31
|
"types"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ndla/article-scripts": "^3.0.
|
|
35
|
-
"@ndla/button": "^3.
|
|
36
|
-
"@ndla/carousel": "^1.2.
|
|
34
|
+
"@ndla/article-scripts": "^3.0.2",
|
|
35
|
+
"@ndla/button": "^3.5.0",
|
|
36
|
+
"@ndla/carousel": "^1.2.22",
|
|
37
37
|
"@ndla/core": "^2.3.5",
|
|
38
|
-
"@ndla/forms": "^3.1
|
|
39
|
-
"@ndla/hooks": "^1.1.
|
|
40
|
-
"@ndla/icons": "^1.
|
|
41
|
-
"@ndla/licenses": "^5.0.
|
|
42
|
-
"@ndla/modal": "^1.3.
|
|
43
|
-
"@ndla/notion": "^3.1.
|
|
44
|
-
"@ndla/safelink": "^2.2.
|
|
38
|
+
"@ndla/forms": "^3.2.1",
|
|
39
|
+
"@ndla/hooks": "^1.1.6",
|
|
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
|
-
"@ndla/util": "^3.0
|
|
49
|
+
"@ndla/util": "^3.1.0",
|
|
50
50
|
"@reach/menu-button": "^0.16.2",
|
|
51
51
|
"@reach/slider": "^0.16.0",
|
|
52
52
|
"focus-trap-react": "^8.9.2",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"publishConfig": {
|
|
86
86
|
"access": "public"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "e2029bce0a42c485f247a60ee895b2320fddc6ba"
|
|
89
89
|
}
|
|
@@ -75,8 +75,11 @@ const getSuppliersText = (suppliers: SupplierProps[], t: TFunction) => {
|
|
|
75
75
|
return '';
|
|
76
76
|
}
|
|
77
77
|
return suppliers.length > 1
|
|
78
|
-
? t('article.multipleSuppliersLabel', {
|
|
79
|
-
|
|
78
|
+
? t('article.multipleSuppliersLabel', {
|
|
79
|
+
names: renderContributors(suppliers, t),
|
|
80
|
+
interpolation: { escapeValue: false },
|
|
81
|
+
})
|
|
82
|
+
: t('article.supplierLabel', { name: renderContributors(suppliers, t), interpolation: { escapeValue: false } });
|
|
80
83
|
};
|
|
81
84
|
|
|
82
85
|
const ArticleByline = ({
|
|
@@ -114,7 +117,10 @@ const ArticleByline = ({
|
|
|
114
117
|
{showPrimaryContributors && (
|
|
115
118
|
<PrimaryContributorsWrapper>
|
|
116
119
|
{authors.length > 0
|
|
117
|
-
? t('article.authorsLabel', {
|
|
120
|
+
? t('article.authorsLabel', {
|
|
121
|
+
names: renderContributors(authors, t),
|
|
122
|
+
interpolation: { escapeValue: false },
|
|
123
|
+
})
|
|
118
124
|
: getSuppliersText(suppliers, t)}
|
|
119
125
|
</PrimaryContributorsWrapper>
|
|
120
126
|
)}
|
|
@@ -150,6 +156,7 @@ const ArticleByline = ({
|
|
|
150
156
|
size="small"
|
|
151
157
|
borderShape="rounded"
|
|
152
158
|
outline
|
|
159
|
+
aria-live="assertive"
|
|
153
160
|
data-copy-string={copyPageUrlLink}
|
|
154
161
|
copyNode={t('article.copyPageLinkCopied')}>
|
|
155
162
|
{t('article.copyPageLink')}
|
|
@@ -92,7 +92,7 @@ const BreadcrumbItem = forwardRef<any, Props>(
|
|
|
92
92
|
const { to, name, index } = item;
|
|
93
93
|
const isLast = index === totalCount - 1;
|
|
94
94
|
return (
|
|
95
|
-
<StyledListItem ref={liRef} autoCollapse={autoCollapse}>
|
|
95
|
+
<StyledListItem ref={liRef} autoCollapse={autoCollapse} aria-current={isLast ? 'page' : undefined}>
|
|
96
96
|
<CollapseContainer autoCollapse={autoCollapse}>
|
|
97
97
|
{renderItem ? (
|
|
98
98
|
renderItem(item, totalCount)
|
|
@@ -47,6 +47,7 @@ interface Props {
|
|
|
47
47
|
linksTitle?: string;
|
|
48
48
|
back?: string;
|
|
49
49
|
goToFrontPage?: string;
|
|
50
|
+
logInFailed?: string;
|
|
50
51
|
};
|
|
51
52
|
illustration?: {
|
|
52
53
|
url: string;
|
|
@@ -78,6 +79,11 @@ export const ErrorMessage = ({ children, messages, illustration, illustrationEle
|
|
|
78
79
|
<SafeLink to="/">{messages.goToFrontPage}</SafeLink>
|
|
79
80
|
</div>
|
|
80
81
|
)}
|
|
82
|
+
{messages.logInFailed && (
|
|
83
|
+
<div css={{ marginTop: spacing.xsmall }}>
|
|
84
|
+
<SafeLink to="/minndla">{messages.logInFailed}</SafeLink>
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
81
87
|
{children}
|
|
82
88
|
</StyledErrorMessage>
|
|
83
89
|
);
|
|
@@ -2,7 +2,6 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
import styled from '@emotion/styled';
|
|
3
3
|
import { colors, spacing, spacingUnit, mq, breakpoints } from '@ndla/core';
|
|
4
4
|
import SafeLink from '@ndla/safelink';
|
|
5
|
-
import { WithTranslation, withTranslation } from 'react-i18next';
|
|
6
5
|
import FrontpageHeaderIllustration from './illustrations/FrontpageHeaderIllustration';
|
|
7
6
|
import SvgLogo from '../Logo/SvgLogo';
|
|
8
7
|
|
|
@@ -48,16 +47,16 @@ const HeaderIllustrationWrapper = styled.div`
|
|
|
48
47
|
}
|
|
49
48
|
`;
|
|
50
49
|
|
|
51
|
-
|
|
50
|
+
interface FrontPageHeaderProps {
|
|
52
51
|
locale: string;
|
|
53
52
|
showHeader: boolean;
|
|
54
53
|
children?: ReactNode;
|
|
55
|
-
}
|
|
54
|
+
}
|
|
56
55
|
|
|
57
|
-
const FrontpageHeader = ({ locale, showHeader = true, children
|
|
56
|
+
const FrontpageHeader = ({ locale, showHeader = true, children }: FrontPageHeaderProps) => (
|
|
58
57
|
<StyledHeaderWrapper>
|
|
59
58
|
<StyledHeader>
|
|
60
|
-
<StyledLogo to="/" aria-
|
|
59
|
+
<StyledLogo to="/" aria-hidden="true">
|
|
61
60
|
<SvgLogo locale={locale} />
|
|
62
61
|
</StyledLogo>
|
|
63
62
|
{showHeader && (
|
|
@@ -70,4 +69,4 @@ const FrontpageHeader = ({ locale, showHeader = true, children, t }: FrontPageHe
|
|
|
70
69
|
</StyledHeaderWrapper>
|
|
71
70
|
);
|
|
72
71
|
|
|
73
|
-
export default
|
|
72
|
+
export default FrontpageHeader;
|
|
@@ -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
|
|
@@ -66,18 +66,19 @@ interface Props {
|
|
|
66
66
|
description?: string;
|
|
67
67
|
title: string;
|
|
68
68
|
invertedStyle?: boolean;
|
|
69
|
+
id?: string;
|
|
69
70
|
license?: {
|
|
70
71
|
license: string;
|
|
71
72
|
};
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
export const LearningPathInformation = ({ description, title, license, invertedStyle }: Props) => {
|
|
75
|
+
export const LearningPathInformation = ({ description, title, license, invertedStyle, id }: Props) => {
|
|
75
76
|
const { rights } = getLicenseByAbbreviation(license?.license || '', 'nb');
|
|
76
77
|
return (
|
|
77
78
|
<section className="o-wrapper">
|
|
78
79
|
<StyledWrapper invertedStyle={invertedStyle} className="c-article">
|
|
79
80
|
<LicenseWrapper>
|
|
80
|
-
<StyledHeader>{title}</StyledHeader>
|
|
81
|
+
<StyledHeader id={id}>{title}</StyledHeader>
|
|
81
82
|
<LicenseByline licenseRights={rights} color={colors.brand.tertiary} />
|
|
82
83
|
</LicenseWrapper>
|
|
83
84
|
{description && <div dangerouslySetInnerHTML={{ __html: description }} />}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import styled from '@emotion/styled';
|
|
3
3
|
import { css } from '@emotion/core';
|
|
4
|
-
import { breakpoints } from '@ndla/
|
|
5
|
-
import { colors, fonts, mq, spacing, spacingUnit } from '@ndla/core';
|
|
4
|
+
import { colors, fonts, mq, spacing, spacingUnit, breakpoints } from '@ndla/core';
|
|
6
5
|
import OneColumn from '../Layout/OneColumn';
|
|
7
6
|
|
|
8
7
|
type Types = 'mobile' | 'tablet' | 'desktop' | 'wide';
|
|
@@ -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
|
|