@ndla/ui 30.8.3 → 31.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/Article/Article.js +3 -3
- package/es/Article/ArticleSideBar.js +5 -5
- package/es/AudioPlayer/Controls.js +26 -26
- package/es/AudioPlayer/SpeechControl.js +1 -1
- package/es/Frontpage/FrontpageSearch.js +2 -9
- package/es/MediaList/MediaList.js +1 -0
- package/es/Messages/MessageBox.js +10 -10
- package/es/NDLAFilm/AllMoviesAlphabetically.js +11 -11
- package/es/NDLAFilm/FilmSlideshow.js +7 -7
- package/es/NDLAFilm/MovieGrid.js +4 -4
- package/es/Programme/Programme.js +5 -5
- package/es/ResourceGroup/ResourceItem.js +12 -13
- package/es/ResourceGroup/ResourceList.js +3 -4
- package/es/Search/SearchField.js +2 -3
- package/es/Subject/SubjectNewContent.js +10 -10
- package/es/TreeStructure/AddFolderButton.js +9 -9
- package/es/TreeStructure/ComboboxButton.js +30 -5
- package/es/TreeStructure/FolderItem.js +11 -10
- package/es/TreeStructure/FolderItems.js +28 -37
- package/es/TreeStructure/TreeStructure.js +9 -7
- package/es/i18n/index.js +1 -2
- package/es/index.js +1 -1
- package/lib/Article/Article.js +3 -3
- package/lib/Article/ArticleSideBar.js +5 -5
- package/lib/AudioPlayer/Controls.js +26 -26
- package/lib/AudioPlayer/SpeechControl.js +1 -1
- package/lib/Frontpage/FrontpageSearch.js +2 -9
- package/lib/MediaList/MediaList.js +1 -0
- package/lib/Messages/MessageBox.js +10 -10
- package/lib/NDLAFilm/AllMoviesAlphabetically.js +11 -11
- package/lib/NDLAFilm/FilmSlideshow.js +7 -7
- package/lib/NDLAFilm/MovieGrid.js +4 -4
- package/lib/Programme/Programme.js +5 -5
- package/lib/ResourceGroup/ResourceItem.js +11 -11
- package/lib/ResourceGroup/ResourceList.js +2 -2
- package/lib/Search/SearchField.js +2 -3
- package/lib/Subject/SubjectNewContent.js +10 -10
- package/lib/TreeStructure/AddFolderButton.d.ts +4 -3
- package/lib/TreeStructure/AddFolderButton.js +9 -9
- package/lib/TreeStructure/ComboboxButton.d.ts +8 -6
- package/lib/TreeStructure/ComboboxButton.js +29 -3
- package/lib/TreeStructure/FolderItem.d.ts +5 -3
- package/lib/TreeStructure/FolderItem.js +11 -10
- package/lib/TreeStructure/FolderItems.d.ts +4 -3
- package/lib/TreeStructure/FolderItems.js +28 -36
- package/lib/TreeStructure/TreeStructure.d.ts +3 -2
- package/lib/TreeStructure/TreeStructure.js +9 -7
- package/lib/TreeStructure/arrowNavigation.d.ts +2 -2
- package/lib/TreeStructure/helperFunctions.d.ts +3 -2
- package/lib/TreeStructure/index.d.ts +0 -1
- package/lib/TreeStructure/types.d.ts +5 -9
- package/lib/i18n/index.d.ts +0 -1
- package/lib/i18n/index.js +1 -8
- package/lib/index.d.ts +2 -2
- package/lib/index.js +0 -7
- package/package.json +10 -12
- package/src/Article/Article.tsx +1 -1
- package/src/Article/ArticleSideBar.tsx +1 -1
- package/src/AudioPlayer/Controls.tsx +1 -0
- package/src/AudioPlayer/SpeechControl.tsx +1 -0
- package/src/Frontpage/FrontpageSearch.tsx +0 -7
- package/src/MediaList/MediaList.tsx +1 -0
- package/src/Messages/MessageBox.tsx +1 -1
- package/src/NDLAFilm/AllMoviesAlphabetically.tsx +1 -1
- package/src/NDLAFilm/FilmSlideshow.tsx +1 -1
- package/src/NDLAFilm/MovieGrid.tsx +2 -1
- package/src/Programme/Programme.tsx +1 -2
- package/src/ResourceGroup/ResourceItem.tsx +1 -2
- package/src/ResourceGroup/ResourceList.tsx +1 -2
- package/src/Search/SearchField.tsx +1 -2
- package/src/Subject/SubjectNewContent.tsx +2 -2
- package/src/TreeStructure/AddFolderButton.tsx +18 -19
- package/src/TreeStructure/ComboboxButton.tsx +52 -26
- package/src/TreeStructure/FolderItem.tsx +10 -7
- package/src/TreeStructure/FolderItems.tsx +31 -36
- package/src/TreeStructure/TreeStructure.tsx +8 -6
- package/src/TreeStructure/arrowNavigation.ts +5 -5
- package/src/TreeStructure/helperFunctions.ts +4 -3
- package/src/TreeStructure/index.ts +0 -1
- package/src/TreeStructure/types.ts +5 -10
- package/src/i18n/index.ts +0 -1
- package/src/index.ts +2 -2
- package/es/TreeStructure/NavigationLink.js +0 -91
- package/es/i18n/formatMessage.js +0 -45
- package/lib/TreeStructure/NavigationLink.d.ts +0 -14
- package/lib/TreeStructure/NavigationLink.js +0 -93
- package/lib/i18n/formatMessage.d.ts +0 -14
- package/lib/i18n/formatMessage.js +0 -53
- package/src/TreeStructure/NavigationLink.tsx +0 -107
- package/src/i18n/__tests__/formatMessage-test.ts +0 -34
- package/src/i18n/formatMessage.ts +0 -61
|
@@ -52,6 +52,7 @@ const SpeechControl = ({ src, title }: Props) => {
|
|
|
52
52
|
};
|
|
53
53
|
return (
|
|
54
54
|
<div>
|
|
55
|
+
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
|
55
56
|
<audio ref={audioRef} src={src} title={title} preload="metadata" />
|
|
56
57
|
<SpeechPlayButton type="button" onClick={togglePlay}>
|
|
57
58
|
<VolumeUp role="img" aria-label="play" title="play" />
|
|
@@ -146,13 +146,6 @@ const FrontpageSearch = ({
|
|
|
146
146
|
}, [inputHasFocus]);
|
|
147
147
|
|
|
148
148
|
const onBlur = () => {
|
|
149
|
-
setTimeout(() => {
|
|
150
|
-
if (searchFieldRef.current) {
|
|
151
|
-
if (!searchFieldRef.current.contains(document.activeElement)) {
|
|
152
|
-
onInputBlur();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}, 0);
|
|
156
149
|
// This is needed when user tabs out of field
|
|
157
150
|
if (!searchFieldValue) {
|
|
158
151
|
onInputBlur();
|
|
@@ -147,6 +147,7 @@ export const MediaListItemMeta = ({ items = [] }: MediaListItemMetaProps) => {
|
|
|
147
147
|
|
|
148
148
|
return (
|
|
149
149
|
//@ts-ignore
|
|
150
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
150
151
|
<ul {...cClasses('actions')} property="cc:attributionName" content={attributionMeta}>
|
|
151
152
|
{items.map((item) => (
|
|
152
153
|
<li key={uuid()} className="c-medialist__meta-item">
|
|
@@ -138,7 +138,7 @@ export const MessageBox = ({ type, children = '', links, showCloseButton, onClos
|
|
|
138
138
|
{links && (
|
|
139
139
|
<LinkWrapper>
|
|
140
140
|
{links.map((x) => (
|
|
141
|
-
<Link href={x.href}>
|
|
141
|
+
<Link href={x.href} key={x.href}>
|
|
142
142
|
<span>{x.text}</span>
|
|
143
143
|
<Forward />
|
|
144
144
|
</Link>
|
|
@@ -170,7 +170,7 @@ const hasForEachPolyfill = () => {
|
|
|
170
170
|
if ('NodeList' in window && !NodeList.prototype.forEach) {
|
|
171
171
|
NodeList.prototype.forEach = function (callback, thisArg) {
|
|
172
172
|
thisArg = thisArg || window;
|
|
173
|
-
for (
|
|
173
|
+
for (let i = 0; i < this.length; i++) {
|
|
174
174
|
callback.call(thisArg, this[i], i, this);
|
|
175
175
|
}
|
|
176
176
|
};
|
|
@@ -217,7 +217,7 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
|
|
|
217
217
|
const [animationComplete, setAnimationComplete] = useState(true);
|
|
218
218
|
const slideRef = useRef<HTMLDivElement>(null);
|
|
219
219
|
const slideText = useRef<HTMLDivElement>(null);
|
|
220
|
-
|
|
220
|
+
const timer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
221
221
|
|
|
222
222
|
const gotoSlide = useCallback((indexTarget: number, useAnimation = false) => {
|
|
223
223
|
setSwipeDistance(0);
|
|
@@ -66,8 +66,9 @@ const MovieGrid = ({
|
|
|
66
66
|
<MovieListing marginLeft={autoSizedProps.margin}>
|
|
67
67
|
{fetchingMoviesByType && <LoadingPlaceholder height={loadingPlaceholderHeight} />}
|
|
68
68
|
{!fetchingMoviesByType &&
|
|
69
|
-
moviesByType.map((movie) => (
|
|
69
|
+
moviesByType.map((movie, index) => (
|
|
70
70
|
<FilmContentCard
|
|
71
|
+
key={index}
|
|
71
72
|
hideTags
|
|
72
73
|
movie={movie}
|
|
73
74
|
columnWidth={autoSizedProps.columnWidth}
|
|
@@ -3,8 +3,7 @@ import styled from '@emotion/styled';
|
|
|
3
3
|
import { breakpoints, mq, spacing } from '@ndla/core';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import LayoutItem, { OneColumn } from '../Layout';
|
|
6
|
-
import ProgrammeSubjects from './ProgrammeSubjects';
|
|
7
|
-
import { GradesProps } from './ProgrammeSubjects';
|
|
6
|
+
import ProgrammeSubjects, { GradesProps } from './ProgrammeSubjects';
|
|
8
7
|
import MessageBox from '../Messages/MessageBox';
|
|
9
8
|
import { NavigationHeading } from '..';
|
|
10
9
|
const StyledWrapper = styled.div`
|
|
@@ -9,8 +9,7 @@
|
|
|
9
9
|
import React, { ReactNode } from 'react';
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import styled from '@emotion/styled';
|
|
12
|
-
import { css } from '@emotion/react';
|
|
13
|
-
import { keyframes } from '@emotion/react';
|
|
12
|
+
import { css, keyframes } from '@emotion/react';
|
|
14
13
|
import SafeLink from '@ndla/safelink';
|
|
15
14
|
import { Additional, Core, HumanMaleBoard } from '@ndla/icons/common';
|
|
16
15
|
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import React, { ReactNode } from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
|
-
import { css } from '@emotion/react';
|
|
12
|
-
import { keyframes } from '@emotion/react';
|
|
11
|
+
import { css, keyframes } from '@emotion/react';
|
|
13
12
|
import { useTranslation } from 'react-i18next';
|
|
14
13
|
import NoContentBox from '../NoContentBox';
|
|
15
14
|
import ResourceItem from './ResourceItem';
|
|
@@ -91,8 +91,8 @@ const SubjectNewContent = ({ heading, content }: Props) => (
|
|
|
91
91
|
<StyledSubjectSectionTitle>{heading}</StyledSubjectSectionTitle>
|
|
92
92
|
<nav>
|
|
93
93
|
<StyledUl>
|
|
94
|
-
{content.map((item) => (
|
|
95
|
-
<StyledListItem>
|
|
94
|
+
{content.map((item, index) => (
|
|
95
|
+
<StyledListItem key={index}>
|
|
96
96
|
<LeftWrapper>
|
|
97
97
|
<ContentTypeBadge type={item.contentType} size="x-small" background border />
|
|
98
98
|
<ContentLinkWrapper>
|
|
@@ -12,13 +12,14 @@ import Tooltip from '@ndla/tooltip';
|
|
|
12
12
|
import styled from '@emotion/styled';
|
|
13
13
|
import { ButtonV2 as Button } from '@ndla/button';
|
|
14
14
|
import { Plus } from '@ndla/icons/action';
|
|
15
|
-
import {
|
|
15
|
+
import { IFolder } from '@ndla/types-learningpath-api';
|
|
16
16
|
|
|
17
17
|
interface AddFolderButtonProps {
|
|
18
18
|
canAddFolder: boolean;
|
|
19
|
-
focusedFolder?:
|
|
19
|
+
focusedFolder?: IFolder;
|
|
20
20
|
setNewFolderParentId: (id?: string) => void;
|
|
21
21
|
setShowTree: (value: boolean) => void;
|
|
22
|
+
loading?: boolean;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
const StyledAddFolderButton = styled(Button)`
|
|
@@ -33,30 +34,28 @@ const StyledPlus = styled(Plus)`
|
|
|
33
34
|
width: 24px;
|
|
34
35
|
`;
|
|
35
36
|
|
|
36
|
-
const AddFolderButton = ({
|
|
37
|
+
const AddFolderButton = ({
|
|
38
|
+
canAddFolder,
|
|
39
|
+
loading,
|
|
40
|
+
setNewFolderParentId,
|
|
41
|
+
focusedFolder,
|
|
42
|
+
setShowTree,
|
|
43
|
+
}: AddFolderButtonProps) => {
|
|
37
44
|
const { t } = useTranslation();
|
|
38
45
|
const ref = useRef<HTMLButtonElement>(null);
|
|
46
|
+
const tooltip = loading
|
|
47
|
+
? t('loading')
|
|
48
|
+
: canAddFolder
|
|
49
|
+
? t('myNdla.newFolderUnder', { folderName: focusedFolder?.name })
|
|
50
|
+
: t('treeStructure.maxFoldersAlreadyAdded');
|
|
39
51
|
return (
|
|
40
|
-
<Tooltip
|
|
41
|
-
tooltip={
|
|
42
|
-
canAddFolder
|
|
43
|
-
? t('myNdla.newFolderUnder', {
|
|
44
|
-
folderName: focusedFolder?.name,
|
|
45
|
-
})
|
|
46
|
-
: t('treeStructure.maxFoldersAlreadyAdded')
|
|
47
|
-
}>
|
|
52
|
+
<Tooltip tooltip={tooltip}>
|
|
48
53
|
<StyledAddFolderButton
|
|
49
54
|
ref={ref}
|
|
50
55
|
variant="outline"
|
|
51
56
|
shape="pill"
|
|
52
|
-
disabled={!canAddFolder}
|
|
53
|
-
aria-label={
|
|
54
|
-
canAddFolder
|
|
55
|
-
? t('myNdla.newFolderUnder', {
|
|
56
|
-
folderName: focusedFolder?.name,
|
|
57
|
-
})
|
|
58
|
-
: t('treeStructure.maxFoldersAlreadyAdded')
|
|
59
|
-
}
|
|
57
|
+
disabled={loading || !canAddFolder}
|
|
58
|
+
aria-label={tooltip}
|
|
60
59
|
onMouseDown={(e) => {
|
|
61
60
|
e.preventDefault();
|
|
62
61
|
e.stopPropagation();
|
|
@@ -6,16 +6,17 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import React, { KeyboardEvent } from 'react';
|
|
9
|
+
import React, { KeyboardEvent, forwardRef } from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
11
|
import { useForwardedRef } from '@ndla/util';
|
|
12
12
|
import { breakpoints, colors, mq, spacing } from '@ndla/core';
|
|
13
13
|
import { ChevronUp, ChevronDown } from '@ndla/icons/common';
|
|
14
|
-
import {
|
|
14
|
+
import { IFolder } from '@ndla/types-learningpath-api';
|
|
15
15
|
import { ButtonV2 as Button, IconButtonV2 as IconButton } from '@ndla/button';
|
|
16
16
|
import { treestructureId } from './helperFunctions';
|
|
17
|
-
import {
|
|
17
|
+
import { TreeStructureType } from './types';
|
|
18
18
|
import { arrowNavigation } from './arrowNavigation';
|
|
19
|
+
import ContentLoader from '../ContentLoader';
|
|
19
20
|
|
|
20
21
|
interface StyledRowProps {
|
|
21
22
|
isOpen: boolean;
|
|
@@ -52,14 +53,15 @@ interface Props {
|
|
|
52
53
|
showTree: boolean;
|
|
53
54
|
type: TreeStructureType;
|
|
54
55
|
label?: string;
|
|
55
|
-
focusedFolder?:
|
|
56
|
-
selectedFolder?:
|
|
57
|
-
setSelectedFolder: (folder:
|
|
56
|
+
focusedFolder?: IFolder;
|
|
57
|
+
selectedFolder?: IFolder;
|
|
58
|
+
setSelectedFolder: (folder: IFolder) => void;
|
|
59
|
+
loading?: boolean;
|
|
58
60
|
onToggleTree: (open: boolean) => void;
|
|
59
|
-
flattenedFolders:
|
|
61
|
+
flattenedFolders: IFolder[];
|
|
60
62
|
onOpenFolder: (id: string) => void;
|
|
61
63
|
onCloseFolder: (id: string) => void;
|
|
62
|
-
setFocusedFolder: (folder:
|
|
64
|
+
setFocusedFolder: (folder: IFolder) => void;
|
|
63
65
|
ariaDescribedby?: string;
|
|
64
66
|
}
|
|
65
67
|
|
|
@@ -77,6 +79,7 @@ const ComboboxButton = forwardRef<HTMLButtonElement, Props>(
|
|
|
77
79
|
setFocusedFolder,
|
|
78
80
|
onOpenFolder,
|
|
79
81
|
onCloseFolder,
|
|
82
|
+
loading,
|
|
80
83
|
ariaDescribedby,
|
|
81
84
|
},
|
|
82
85
|
ref,
|
|
@@ -116,25 +119,48 @@ const ComboboxButton = forwardRef<HTMLButtonElement, Props>(
|
|
|
116
119
|
innerRef.current?.focus();
|
|
117
120
|
}
|
|
118
121
|
}}>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
{loading && (
|
|
123
|
+
<ContentLoader width={1000} height={40}>
|
|
124
|
+
<rect x="15" y="0" width="1000" rx="3" ry="3" r="15" height="40" />
|
|
125
|
+
</ContentLoader>
|
|
126
|
+
)}
|
|
127
|
+
{!loading && (
|
|
128
|
+
<StyledSelectedFolder
|
|
129
|
+
ref={innerRef}
|
|
130
|
+
tabIndex={0}
|
|
131
|
+
id={treestructureId(type, 'combobox')}
|
|
132
|
+
role="combobox"
|
|
133
|
+
aria-controls={treestructureId(type, 'popup')}
|
|
134
|
+
aria-haspopup="tree"
|
|
135
|
+
aria-expanded={showTree}
|
|
136
|
+
aria-labelledby={label ? treestructureId(type, 'label') : undefined}
|
|
137
|
+
aria-activedescendant={focusedFolder ? treestructureId(type, focusedFolder.id) : undefined}
|
|
138
|
+
aria-describedby={ariaDescribedby}
|
|
139
|
+
variant="ghost"
|
|
140
|
+
colorTheme="light"
|
|
141
|
+
fontWeight="normal"
|
|
142
|
+
shape="sharp"
|
|
143
|
+
onKeyDown={onKeyDown}
|
|
144
|
+
onClick={() => {
|
|
145
|
+
innerRef.current?.focus();
|
|
146
|
+
onToggleTree(!showTree);
|
|
147
|
+
}}>
|
|
148
|
+
{selectedFolder?.name}
|
|
149
|
+
</StyledSelectedFolder>
|
|
150
|
+
)}
|
|
151
|
+
<IconButton
|
|
152
|
+
disabled={loading}
|
|
153
|
+
aria-busy={loading}
|
|
154
|
+
aria-hidden
|
|
155
|
+
aria-label=""
|
|
156
|
+
tabIndex={-1}
|
|
130
157
|
variant="ghost"
|
|
131
|
-
colorTheme="
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
<IconButton aria-hidden aria-label="" tabIndex={-1} variant="ghost" colorTheme="greyLighter" size="small">
|
|
158
|
+
colorTheme="greyLighter"
|
|
159
|
+
size="small"
|
|
160
|
+
onClick={() => {
|
|
161
|
+
innerRef.current?.focus();
|
|
162
|
+
onToggleTree(!showTree);
|
|
163
|
+
}}>
|
|
138
164
|
{showTree ? <ChevronUp /> : <ChevronDown />}
|
|
139
165
|
</IconButton>
|
|
140
166
|
</StyledRow>
|
|
@@ -14,7 +14,8 @@ import { Done } from '@ndla/icons/editor';
|
|
|
14
14
|
import { ButtonV2 as Button } from '@ndla/button';
|
|
15
15
|
import { colors, spacing, animations, spacingUnit, misc, fonts } from '@ndla/core';
|
|
16
16
|
import SafeLink from '@ndla/safelink';
|
|
17
|
-
import {
|
|
17
|
+
import { IFolder } from '@ndla/types-learningpath-api';
|
|
18
|
+
import { CommonFolderItemsProps } from './types';
|
|
18
19
|
import { arrowNavigation } from './arrowNavigation';
|
|
19
20
|
import { treestructureId } from './helperFunctions';
|
|
20
21
|
|
|
@@ -104,8 +105,9 @@ const FolderNameLink = styled(SafeLink, { shouldForwardProp })<FolderNameProps>`
|
|
|
104
105
|
|
|
105
106
|
interface Props extends CommonFolderItemsProps {
|
|
106
107
|
isOpen: boolean;
|
|
107
|
-
folder:
|
|
108
|
+
folder: IFolder;
|
|
108
109
|
isCreatingFolder?: boolean;
|
|
110
|
+
index: number;
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
const FolderItem = ({
|
|
@@ -125,9 +127,10 @@ const FolderItem = ({
|
|
|
125
127
|
isCreatingFolder,
|
|
126
128
|
type,
|
|
127
129
|
closeTree,
|
|
130
|
+
index,
|
|
128
131
|
}: Props) => {
|
|
129
132
|
const { t } = useTranslation();
|
|
130
|
-
const { id, name
|
|
133
|
+
const { id, name } = folder;
|
|
131
134
|
const ref = useRef<HTMLButtonElement & HTMLAnchorElement>(null);
|
|
132
135
|
const selected = selectedFolder ? selectedFolder.id === id : false;
|
|
133
136
|
|
|
@@ -159,17 +162,17 @@ const FolderItem = ({
|
|
|
159
162
|
}
|
|
160
163
|
}, [focusedFolder, ref, id, isCreatingFolder, type]);
|
|
161
164
|
|
|
162
|
-
const linkPath = `/minndla
|
|
165
|
+
const linkPath = `/minndla/folders/${id}`;
|
|
163
166
|
|
|
164
167
|
const containsResource =
|
|
165
168
|
targetResource && folder.resources.some((resource) => resource.resourceId === targetResource.resourceId);
|
|
166
169
|
|
|
167
170
|
const emptyFolder = folder.subfolders.length === 0;
|
|
168
|
-
|
|
169
171
|
const isMaxDepth = level > maxLevel;
|
|
170
|
-
|
|
171
172
|
const hideArrow = isMaxDepth || emptyFolder;
|
|
172
173
|
|
|
174
|
+
const tabable = selected || focused || (!focusedFolder && !folder.parentId && index === 0);
|
|
175
|
+
|
|
173
176
|
return type === 'navigation' ? (
|
|
174
177
|
<FolderNameLink
|
|
175
178
|
role="treeitem"
|
|
@@ -187,7 +190,7 @@ const FolderItem = ({
|
|
|
187
190
|
arrowNavigation(e, id, visibleFolders, setFocusedFolder, onOpenFolder, onCloseFolder);
|
|
188
191
|
}}
|
|
189
192
|
to={loading ? '' : linkPath}
|
|
190
|
-
tabIndex={
|
|
193
|
+
tabIndex={tabable ? 0 : -1}
|
|
191
194
|
selected={selected}
|
|
192
195
|
onFocus={() => setFocusedFolder(folder)}
|
|
193
196
|
onClick={handleClickFolder}>
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
import React, { ReactNode } from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
11
|
import { animations } from '@ndla/core';
|
|
12
|
+
import { IFolder } from '@ndla/types-learningpath-api';
|
|
12
13
|
import FolderItem from './FolderItem';
|
|
13
|
-
import { CommonFolderItemsProps,
|
|
14
|
-
import NavigationLink from './NavigationLink';
|
|
14
|
+
import { CommonFolderItemsProps, NewFolderInputFunc, OnCreatedFunc, TreeStructureType } from './types';
|
|
15
15
|
import { treestructureId } from './helperFunctions';
|
|
16
16
|
|
|
17
17
|
const StyledUL = styled.ul`
|
|
@@ -38,11 +38,11 @@ const StyledLI = styled.li<StyledLiProps>`
|
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
40
|
export interface FolderItemsProps extends CommonFolderItemsProps {
|
|
41
|
-
folders:
|
|
41
|
+
folders: IFolder[];
|
|
42
42
|
newFolderParentId: string | undefined;
|
|
43
43
|
onCancelNewFolder: () => void;
|
|
44
44
|
openFolders: string[];
|
|
45
|
-
parentFolder?:
|
|
45
|
+
parentFolder?: IFolder;
|
|
46
46
|
children?: ReactNode;
|
|
47
47
|
onCreate: OnCreatedFunc;
|
|
48
48
|
newFolderInput?: NewFolderInputFunc;
|
|
@@ -74,44 +74,39 @@ const FolderItems = ({
|
|
|
74
74
|
aria-labelledby={level === 0 && type === 'picker' ? treestructureId(type, 'label') : undefined}
|
|
75
75
|
role={level === 0 ? 'tree' : 'group'}>
|
|
76
76
|
{children}
|
|
77
|
-
{folders.map((folder) => {
|
|
77
|
+
{folders.map((folder, index) => {
|
|
78
78
|
const { subfolders, id } = folder;
|
|
79
79
|
const isOpen = openFolders?.includes(id);
|
|
80
80
|
|
|
81
81
|
return (
|
|
82
82
|
<StyledLI key={id} tabIndex={-1} role="none" type={type}>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
{...rest}>
|
|
109
|
-
{newFolderParentId === id && (
|
|
110
|
-
<li role="none">{newFolderInput?.({ parentId: id, onClose: onCancelNewFolder, onCreate })}</li>
|
|
111
|
-
)}
|
|
112
|
-
</FolderItems>
|
|
83
|
+
<FolderItem
|
|
84
|
+
index={index}
|
|
85
|
+
folder={folder}
|
|
86
|
+
isOpen={isOpen}
|
|
87
|
+
level={level}
|
|
88
|
+
loading={loading}
|
|
89
|
+
type={type}
|
|
90
|
+
isCreatingFolder={!!newFolderParentId}
|
|
91
|
+
{...rest}
|
|
92
|
+
/>
|
|
93
|
+
{((subfolders && isOpen) || newFolderParentId === id) && (
|
|
94
|
+
<FolderItems
|
|
95
|
+
parentFolder={folder}
|
|
96
|
+
folders={subfolders}
|
|
97
|
+
level={level + 1}
|
|
98
|
+
loading={loading}
|
|
99
|
+
type={type}
|
|
100
|
+
newFolderParentId={newFolderParentId}
|
|
101
|
+
onCancelNewFolder={onCancelNewFolder}
|
|
102
|
+
openFolders={openFolders}
|
|
103
|
+
newFolderInput={newFolderInput}
|
|
104
|
+
onCreate={onCreate}
|
|
105
|
+
{...rest}>
|
|
106
|
+
{newFolderParentId === id && (
|
|
107
|
+
<li role="none">{newFolderInput?.({ parentId: id, onClose: onCancelNewFolder, onCreate })}</li>
|
|
113
108
|
)}
|
|
114
|
-
|
|
109
|
+
</FolderItems>
|
|
115
110
|
)}
|
|
116
111
|
</StyledLI>
|
|
117
112
|
);
|
|
@@ -14,7 +14,7 @@ import { uniq } from 'lodash';
|
|
|
14
14
|
import { IFolder } from '@ndla/types-learningpath-api';
|
|
15
15
|
import FolderItems from './FolderItems';
|
|
16
16
|
import { flattenFolders, treestructureId } from './helperFunctions';
|
|
17
|
-
import { CommonTreeStructureProps,
|
|
17
|
+
import { CommonTreeStructureProps, NewFolderInputFunc, TreeStructureType } from './types';
|
|
18
18
|
import ComboboxButton from './ComboboxButton';
|
|
19
19
|
import AddFolderButton from './AddFolderButton';
|
|
20
20
|
|
|
@@ -69,7 +69,7 @@ const ScrollableDiv = styled.div<ScrollableDivProps>`
|
|
|
69
69
|
|
|
70
70
|
export interface TreeStructureProps extends CommonTreeStructureProps {
|
|
71
71
|
defaultOpenFolders?: string[];
|
|
72
|
-
folders:
|
|
72
|
+
folders: IFolder[];
|
|
73
73
|
label?: string;
|
|
74
74
|
maxLevel?: number;
|
|
75
75
|
newFolderInput?: NewFolderInputFunc;
|
|
@@ -96,8 +96,8 @@ const TreeStructure = ({
|
|
|
96
96
|
const [openFolders, setOpenFolders] = useState<string[]>(defaultOpenFolders || []);
|
|
97
97
|
|
|
98
98
|
const [newFolderParentId, setNewFolderParentId] = useState<string | undefined>();
|
|
99
|
-
const [focusedFolder, _setFocusedFolder] = useState<
|
|
100
|
-
const [selectedFolder, _setSelectedFolder] = useState<
|
|
99
|
+
const [focusedFolder, _setFocusedFolder] = useState<IFolder | undefined>();
|
|
100
|
+
const [selectedFolder, _setSelectedFolder] = useState<IFolder | undefined>();
|
|
101
101
|
const [showTree, setShowTree] = useState(type === 'navigation');
|
|
102
102
|
|
|
103
103
|
const flattenedFolders = useMemo(() => flattenFolders(folders, openFolders), [folders, openFolders]);
|
|
@@ -133,12 +133,12 @@ const TreeStructure = ({
|
|
|
133
133
|
}
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
-
const setSelectedFolder = (folder:
|
|
136
|
+
const setSelectedFolder = (folder: IFolder) => {
|
|
137
137
|
_setSelectedFolder(folder);
|
|
138
138
|
onSelectFolder?.(folder.id);
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
-
const setFocusedFolder = (folder:
|
|
141
|
+
const setFocusedFolder = (folder: IFolder) => {
|
|
142
142
|
_setFocusedFolder(folder);
|
|
143
143
|
setNewFolderParentId(undefined);
|
|
144
144
|
|
|
@@ -189,6 +189,7 @@ const TreeStructure = ({
|
|
|
189
189
|
{label && <StyledLabel id={treestructureId(type, 'label')}>{label}</StyledLabel>}
|
|
190
190
|
{type === 'picker' && (
|
|
191
191
|
<AddFolderButton
|
|
192
|
+
loading={loading}
|
|
192
193
|
canAddFolder={!!canAddFolder}
|
|
193
194
|
focusedFolder={focusedFolder}
|
|
194
195
|
setNewFolderParentId={setNewFolderParentId}
|
|
@@ -203,6 +204,7 @@ const TreeStructure = ({
|
|
|
203
204
|
showTree={showTree}
|
|
204
205
|
type={type}
|
|
205
206
|
label={label}
|
|
207
|
+
loading={loading}
|
|
206
208
|
focusedFolder={focusedFolder}
|
|
207
209
|
selectedFolder={selectedFolder}
|
|
208
210
|
setSelectedFolder={setSelectedFolder}
|
|
@@ -6,13 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { IFolder } from '@ndla/types-learningpath-api';
|
|
9
10
|
import { KeyboardEvent } from 'react';
|
|
10
|
-
import { FolderType } from './types';
|
|
11
11
|
|
|
12
12
|
const navigateVertical = (
|
|
13
|
-
visibleFolders:
|
|
13
|
+
visibleFolders: IFolder[],
|
|
14
14
|
folderId: string,
|
|
15
|
-
setFocusedFolderId: (id:
|
|
15
|
+
setFocusedFolderId: (id: IFolder) => void,
|
|
16
16
|
direction: 1 | -1,
|
|
17
17
|
) => {
|
|
18
18
|
const currentIndex = visibleFolders.findIndex((folder) => folder.id === folderId);
|
|
@@ -27,8 +27,8 @@ const arrowKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Enter'];
|
|
|
27
27
|
export const arrowNavigation = (
|
|
28
28
|
e: KeyboardEvent<HTMLElement>,
|
|
29
29
|
id: string,
|
|
30
|
-
visibleFolders:
|
|
31
|
-
setFocusedFolderId: (id:
|
|
30
|
+
visibleFolders: IFolder[],
|
|
31
|
+
setFocusedFolderId: (id: IFolder) => void,
|
|
32
32
|
onOpen: (id: string) => void,
|
|
33
33
|
onClose: (id: string) => void,
|
|
34
34
|
) => {
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IFolder } from '@ndla/types-learningpath-api';
|
|
2
|
+
import { TreeStructureType } from './types';
|
|
2
3
|
|
|
3
|
-
export const flattenFolders = (folders:
|
|
4
|
+
export const flattenFolders = (folders: IFolder[], openFolders?: string[]): IFolder[] => {
|
|
4
5
|
return folders.reduce((acc, { subfolders, id, ...rest }) => {
|
|
5
6
|
if (!subfolders || (openFolders && !openFolders.includes(id))) {
|
|
6
7
|
return acc.concat({ subfolders, id, ...rest });
|
|
7
8
|
}
|
|
8
9
|
return acc.concat({ subfolders, id, ...rest }, flattenFolders(subfolders, openFolders));
|
|
9
|
-
}, [] as
|
|
10
|
+
}, [] as IFolder[]);
|
|
10
11
|
};
|
|
11
12
|
|
|
12
13
|
export const treestructureId = (type: TreeStructureType, modifier: string) => {
|
|
@@ -9,11 +9,6 @@
|
|
|
9
9
|
import { ReactNode } from 'react';
|
|
10
10
|
import { IFolder, IResource } from '@ndla/types-learningpath-api';
|
|
11
11
|
|
|
12
|
-
export interface FolderType extends IFolder {
|
|
13
|
-
icon?: ReactNode;
|
|
14
|
-
isNavigation?: boolean;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
12
|
export type TreeStructureType = 'navigation' | 'picker';
|
|
18
13
|
|
|
19
14
|
export type OnCreatedFunc = (folder: IFolder | undefined, parentId: string) => void;
|
|
@@ -35,14 +30,14 @@ export interface CommonTreeStructureProps {
|
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
export interface CommonFolderItemsProps extends CommonTreeStructureProps {
|
|
38
|
-
focusedFolder?:
|
|
33
|
+
focusedFolder?: IFolder;
|
|
39
34
|
level: number;
|
|
40
35
|
maxLevel: number;
|
|
41
|
-
selectedFolder?:
|
|
36
|
+
selectedFolder?: IFolder;
|
|
42
37
|
onCloseFolder: (id: string) => void;
|
|
43
38
|
onOpenFolder: (id: string) => void;
|
|
44
|
-
setFocusedFolder: (folder:
|
|
45
|
-
setSelectedFolder: (folder:
|
|
46
|
-
visibleFolders:
|
|
39
|
+
setFocusedFolder: (folder: IFolder) => void;
|
|
40
|
+
setSelectedFolder: (folder: IFolder) => void;
|
|
41
|
+
visibleFolders: IFolder[];
|
|
47
42
|
closeTree: () => void;
|
|
48
43
|
}
|
package/src/i18n/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -174,7 +174,7 @@ export { default as Breadcrumb, HeaderBreadcrumb, HomeBreadcrumb, ActionBreadcru
|
|
|
174
174
|
export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './Breadcrumb';
|
|
175
175
|
|
|
176
176
|
export type { BreadcrumbItemProps } from './Breadcrumblist';
|
|
177
|
-
export { i18nInstance, formatNestedMessages
|
|
177
|
+
export { i18nInstance, formatNestedMessages } from './i18n';
|
|
178
178
|
export { default as ResourceGroup } from './ResourceGroup';
|
|
179
179
|
|
|
180
180
|
export { default as LayoutItem, OneColumn, PageContainer, Content } from './Layout';
|
|
@@ -251,7 +251,7 @@ export { SnackbarProvider, useSnack, BaseSnack, DefaultSnackbar } from './SnackB
|
|
|
251
251
|
export type { Snack, SnackContext } from './SnackBar';
|
|
252
252
|
export { InfoBlock } from './InfoBlock';
|
|
253
253
|
export { TreeStructure } from './TreeStructure';
|
|
254
|
-
export type {
|
|
254
|
+
export type { TreeStructureProps } from './TreeStructure';
|
|
255
255
|
|
|
256
256
|
export { SearchField, SearchResultList, SearchResultItem, ActiveFilters, ToggleSearchButton } from './Search';
|
|
257
257
|
export { default as LetterFilter } from './LetterFilter';
|