@ndla/ui 14.0.0 → 15.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/es/Article/Article.js +22 -3
  2. package/es/Article/ArticleFavoritesButton.js +38 -0
  3. package/es/Article/index.js +2 -1
  4. package/es/Breadcrumb/ActionBreadcrumb.js +57 -0
  5. package/es/Breadcrumb/index.js +1 -0
  6. package/es/InfoBlock/InfoBlock.js +55 -0
  7. package/es/InfoBlock/index.js +1 -0
  8. package/es/MyNdla/Navigation/VerticalNavigation.js +51 -0
  9. package/es/MyNdla/Navigation/index.js +2 -0
  10. package/es/MyNdla/Resource/Folder.js +86 -0
  11. package/es/MyNdla/Resource/FolderInput.js +96 -0
  12. package/{lib/MyNdla/ResourceDash/ResourcesView.d.ts → es/MyNdla/Resource/index.js} +3 -3
  13. package/es/MyNdla/index.js +4 -4
  14. package/es/Resource/BlockResource.js +73 -0
  15. package/es/Resource/ListResource.js +66 -0
  16. package/es/Resource/index.js +10 -0
  17. package/es/Resource/resourceComponents.js +97 -0
  18. package/es/ResourceGroup/ResourceGroup.js +7 -5
  19. package/es/ResourceGroup/ResourceItem.js +25 -24
  20. package/es/ResourceGroup/ResourceList.js +18 -6
  21. package/es/SnackBar/SnackBar.js +117 -0
  22. package/es/SnackBar/index.js +9 -0
  23. package/es/TagSelector/SuggestionInput.js +240 -0
  24. package/es/TagSelector/Suggestions.js +93 -0
  25. package/es/TagSelector/TagSelector.js +137 -0
  26. package/es/TagSelector/index.js +9 -0
  27. package/es/TreeStructure/FolderItem.js +130 -0
  28. package/es/TreeStructure/FolderItems.js +123 -0
  29. package/es/TreeStructure/FolderNameInput.js +112 -0
  30. package/es/TreeStructure/TreeStructure.js +254 -0
  31. package/es/TreeStructure/TreeStructure.types.js +0 -0
  32. package/es/TreeStructure/TreeStructureWrapper.js +13 -0
  33. package/es/TreeStructure/helperFunctions.js +92 -0
  34. package/es/TreeStructure/index.js +9 -0
  35. package/es/TreeStructure/keyboardNavigation/keyboardNavigation.js +182 -0
  36. package/es/TreeStructure/keyboardNavigation/keyboardNavigation.types.js +0 -0
  37. package/es/all.css +72 -0
  38. package/es/index.js +8 -3
  39. package/es/locale/messages-en.js +62 -4
  40. package/es/locale/messages-nb.js +61 -3
  41. package/es/locale/messages-nn.js +61 -3
  42. package/es/locale/messages-se.js +61 -3
  43. package/es/locale/messages-sma.js +61 -3
  44. package/lib/Article/Article.d.ts +3 -1
  45. package/lib/Article/Article.js +43 -23
  46. package/lib/Article/ArticleFavoritesButton.d.ts +15 -0
  47. package/lib/Article/ArticleFavoritesButton.js +56 -0
  48. package/lib/Article/index.d.ts +2 -1
  49. package/lib/Article/index.js +8 -0
  50. package/lib/Breadcrumb/ActionBreadcrumb.d.ts +16 -0
  51. package/lib/Breadcrumb/ActionBreadcrumb.js +72 -0
  52. package/lib/Breadcrumb/index.d.ts +1 -0
  53. package/lib/Breadcrumb/index.js +8 -0
  54. package/lib/InfoBlock/InfoBlock.d.ts +8 -0
  55. package/lib/InfoBlock/InfoBlock.js +58 -0
  56. package/lib/InfoBlock/index.d.ts +1 -0
  57. package/lib/InfoBlock/index.js +13 -0
  58. package/lib/MyNdla/Navigation/VerticalNavigation.d.ts +10 -0
  59. package/lib/MyNdla/Navigation/VerticalNavigation.js +61 -0
  60. package/lib/MyNdla/Navigation/index.d.ts +2 -0
  61. package/lib/MyNdla/Navigation/index.js +15 -0
  62. package/lib/MyNdla/Resource/Folder.d.ts +20 -0
  63. package/lib/MyNdla/Resource/Folder.js +100 -0
  64. package/lib/MyNdla/Resource/FolderInput.d.ts +15 -0
  65. package/lib/MyNdla/Resource/FolderInput.js +116 -0
  66. package/lib/MyNdla/Resource/index.d.ts +10 -0
  67. package/lib/MyNdla/Resource/index.js +23 -0
  68. package/lib/MyNdla/index.d.ts +4 -4
  69. package/lib/MyNdla/index.js +13 -7
  70. package/lib/Resource/BlockResource.d.ts +20 -0
  71. package/lib/Resource/BlockResource.js +84 -0
  72. package/lib/Resource/ListResource.d.ts +20 -0
  73. package/lib/Resource/ListResource.js +78 -0
  74. package/lib/Resource/index.d.ts +11 -0
  75. package/lib/Resource/index.js +29 -0
  76. package/lib/Resource/resourceComponents.d.ts +24 -0
  77. package/lib/Resource/resourceComponents.js +106 -0
  78. package/lib/ResourceGroup/ResourceGroup.d.ts +2 -1
  79. package/lib/ResourceGroup/ResourceGroup.js +7 -5
  80. package/lib/ResourceGroup/ResourceItem.d.ts +5 -1
  81. package/lib/ResourceGroup/ResourceItem.js +26 -24
  82. package/lib/ResourceGroup/ResourceList.d.ts +3 -1
  83. package/lib/ResourceGroup/ResourceList.js +18 -6
  84. package/lib/SnackBar/SnackBar.d.ts +23 -0
  85. package/lib/SnackBar/SnackBar.js +127 -0
  86. package/lib/SnackBar/index.d.ts +10 -0
  87. package/lib/SnackBar/index.js +15 -0
  88. package/lib/TagSelector/SuggestionInput.d.ts +19 -0
  89. package/lib/TagSelector/SuggestionInput.js +255 -0
  90. package/lib/TagSelector/Suggestions.d.ts +12 -0
  91. package/lib/TagSelector/Suggestions.js +96 -0
  92. package/lib/TagSelector/TagSelector.d.ts +16 -0
  93. package/lib/TagSelector/TagSelector.js +150 -0
  94. package/lib/TagSelector/index.d.ts +10 -0
  95. package/lib/TagSelector/index.js +19 -0
  96. package/lib/TreeStructure/FolderItem.d.ts +27 -0
  97. package/lib/TreeStructure/FolderItem.js +140 -0
  98. package/lib/TreeStructure/FolderItems.d.ts +11 -0
  99. package/lib/TreeStructure/FolderItems.js +130 -0
  100. package/lib/TreeStructure/FolderNameInput.d.ts +15 -0
  101. package/lib/TreeStructure/FolderNameInput.js +125 -0
  102. package/lib/TreeStructure/TreeStructure.d.ts +12 -0
  103. package/lib/TreeStructure/TreeStructure.js +273 -0
  104. package/lib/TreeStructure/TreeStructure.types.d.ts +63 -0
  105. package/lib/TreeStructure/TreeStructure.types.js +1 -0
  106. package/lib/TreeStructure/TreeStructureWrapper.d.ts +12 -0
  107. package/lib/TreeStructure/TreeStructureWrapper.js +24 -0
  108. package/lib/TreeStructure/helperFunctions.d.ts +5 -0
  109. package/lib/TreeStructure/helperFunctions.js +103 -0
  110. package/lib/TreeStructure/index.d.ts +10 -0
  111. package/lib/TreeStructure/index.js +15 -0
  112. package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.d.ts +11 -0
  113. package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.js +186 -0
  114. package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.types.d.ts +26 -0
  115. package/lib/TreeStructure/keyboardNavigation/keyboardNavigation.types.js +1 -0
  116. package/lib/User/apiTypes.d.ts +1 -1
  117. package/lib/User/index.d.ts +2 -2
  118. package/lib/all.css +72 -0
  119. package/lib/index.d.ts +13 -4
  120. package/lib/index.js +75 -9
  121. package/lib/locale/messages-en.d.ts +58 -0
  122. package/lib/locale/messages-en.js +62 -4
  123. package/lib/locale/messages-nb.d.ts +58 -0
  124. package/lib/locale/messages-nb.js +61 -3
  125. package/lib/locale/messages-nn.d.ts +58 -0
  126. package/lib/locale/messages-nn.js +61 -3
  127. package/lib/locale/messages-se.d.ts +58 -0
  128. package/lib/locale/messages-se.js +61 -3
  129. package/lib/locale/messages-sma.d.ts +58 -0
  130. package/lib/locale/messages-sma.js +61 -3
  131. package/lib/types.d.ts +1 -1
  132. package/package.json +12 -11
  133. package/src/Article/Article.tsx +31 -0
  134. package/src/Article/ArticleFavoritesButton.tsx +40 -0
  135. package/src/Article/index.ts +2 -0
  136. package/src/Breadcrumb/ActionBreadcrumb.tsx +68 -0
  137. package/src/Breadcrumb/index.ts +2 -0
  138. package/src/InfoBlock/InfoBlock.tsx +61 -0
  139. package/src/InfoBlock/index.ts +1 -0
  140. package/src/MyNdla/Navigation/VerticalNavigation.tsx +93 -0
  141. package/src/MyNdla/Navigation/index.ts +2 -0
  142. package/src/MyNdla/Resource/Folder.tsx +145 -0
  143. package/src/MyNdla/Resource/FolderInput.tsx +104 -0
  144. package/src/MyNdla/Resource/index.ts +11 -0
  145. package/src/MyNdla/index.ts +4 -5
  146. package/src/Resource/BlockResource.tsx +101 -0
  147. package/src/Resource/ListResource.tsx +111 -0
  148. package/src/Resource/index.ts +12 -0
  149. package/src/Resource/resourceComponents.tsx +143 -0
  150. package/src/ResourceGroup/ResourceGroup.tsx +3 -0
  151. package/src/ResourceGroup/ResourceItem.tsx +17 -0
  152. package/src/ResourceGroup/ResourceList.tsx +16 -3
  153. package/src/SnackBar/SnackBar.tsx +183 -0
  154. package/src/SnackBar/index.ts +13 -0
  155. package/src/TagSelector/SuggestionInput.tsx +230 -0
  156. package/src/TagSelector/Suggestions.tsx +125 -0
  157. package/src/TagSelector/TagSelector.tsx +111 -0
  158. package/src/TagSelector/index.ts +13 -0
  159. package/src/TreeStructure/FolderItem.tsx +160 -0
  160. package/src/TreeStructure/FolderItems.tsx +109 -0
  161. package/src/TreeStructure/FolderNameInput.tsx +109 -0
  162. package/src/TreeStructure/TreeStructure.tsx +184 -0
  163. package/src/TreeStructure/TreeStructure.types.ts +69 -0
  164. package/src/TreeStructure/TreeStructureWrapper.tsx +34 -0
  165. package/src/TreeStructure/helperFunctions.ts +52 -0
  166. package/src/TreeStructure/index.ts +11 -0
  167. package/src/TreeStructure/keyboardNavigation/keyboardNavigation.ts +161 -0
  168. package/src/TreeStructure/keyboardNavigation/keyboardNavigation.types.ts +28 -0
  169. package/src/User/apiTypes.ts +1 -1
  170. package/src/User/index.ts +2 -2
  171. package/src/all.scss +1 -0
  172. package/src/index.ts +14 -5
  173. package/src/locale/messages-en.ts +56 -3
  174. package/src/locale/messages-nb.ts +55 -2
  175. package/src/locale/messages-nn.ts +55 -2
  176. package/src/locale/messages-se.ts +55 -2
  177. package/src/locale/messages-sma.ts +55 -2
  178. package/src/types.ts +1 -1
  179. package/es/MyNdla/ResourceDash/Breadcrumbs.js +0 -22
  180. package/es/MyNdla/ResourceDash/ResourceElement.js +0 -27
  181. package/es/MyNdla/ResourceDash/ResourcesView.js +0 -43
  182. package/es/MyNdla/ResourceDash/index.js +0 -4
  183. package/lib/MyNdla/ResourceDash/Breadcrumbs.d.ts +0 -15
  184. package/lib/MyNdla/ResourceDash/Breadcrumbs.js +0 -35
  185. package/lib/MyNdla/ResourceDash/ResourceElement.d.ts +0 -18
  186. package/lib/MyNdla/ResourceDash/ResourceElement.js +0 -38
  187. package/lib/MyNdla/ResourceDash/ResourcesView.js +0 -57
  188. package/lib/MyNdla/ResourceDash/index.d.ts +0 -4
  189. package/lib/MyNdla/ResourceDash/index.js +0 -31
  190. package/src/MyNdla/ResourceDash/Breadcrumbs.tsx +0 -31
  191. package/src/MyNdla/ResourceDash/ResourceElement.tsx +0 -50
  192. package/src/MyNdla/ResourceDash/ResourcesView.tsx +0 -42
  193. package/src/MyNdla/ResourceDash/index.ts +0 -5
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import React from 'react';
10
+
11
+ export interface FolderStructureProps {
12
+ id: string;
13
+ name: string;
14
+ isOpen?: boolean;
15
+ data?: FolderStructureProps[];
16
+ isFavorite?: boolean;
17
+ status?: string;
18
+ openAsDefault?: boolean;
19
+ url?: string;
20
+ icon?: React.ReactNode;
21
+ }
22
+
23
+ export interface NewFolderProps {
24
+ parentId?: string;
25
+ idPaths: number[];
26
+ }
27
+
28
+ interface CommonFolderProps {
29
+ data: FolderStructureProps[];
30
+ editable?: boolean;
31
+ loading?: boolean;
32
+ openOnFolderClick?: boolean;
33
+ }
34
+
35
+ export interface TreeStructureProps extends CommonFolderProps {
36
+ framed?: boolean;
37
+ label: string;
38
+ folderIdMarkedByDefault?: string;
39
+ onNewFolder: (props: { value: string; parentId?: string; idPaths: number[] }) => Promise<string>;
40
+ defaultOpenFolders?: string[];
41
+ }
42
+
43
+ export type onCreateNewFolderProp = ({
44
+ idPaths,
45
+ parentId,
46
+ }: {
47
+ idPaths: number[];
48
+ parentId: string | undefined;
49
+ }) => void;
50
+
51
+ export type SetOpenFolderProp = React.Dispatch<React.SetStateAction<Set<string>>>;
52
+ export type SetFocusedFolderId = React.Dispatch<React.SetStateAction<string | undefined>>;
53
+
54
+ export interface FolderItemsProps extends CommonFolderProps {
55
+ onToggleOpen: (id: string) => void;
56
+ onSaveNewFolder: (value: string) => void;
57
+ onCancelNewFolder: () => void;
58
+ onCreateNewFolder: onCreateNewFolderProp;
59
+ newFolder: NewFolderProps | undefined;
60
+ openFolders: Set<string>;
61
+ markedFolderId?: string;
62
+ onMarkFolder: (id: string) => void;
63
+ idPaths: number[];
64
+ focusedFolderId: string | undefined;
65
+ setFocusedFolderId: SetFocusedFolderId;
66
+ firstLevel: boolean;
67
+ keyNavigationFocusIsCreateFolderButton?: boolean;
68
+ icon?: React.ReactElement;
69
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import styled from '@emotion/styled';
10
+ import { css } from '@emotion/core';
11
+ import { colors, misc, spacing } from '@ndla/core';
12
+
13
+ const TreeStructureWrapper = styled.div<{ framed?: boolean }>`
14
+ padding: ${spacing.xsmall};
15
+ ${({ framed }) =>
16
+ framed
17
+ ? css`
18
+ border: 1px solid ${colors.brand.greyLighter};
19
+ border-radius: ${misc.borderRadius};
20
+ max-height: 400px;
21
+ overflow-y: scroll;
22
+ scroll-behavior: smooth;
23
+ padding: ${spacing.small};
24
+ `
25
+ : css`
26
+ margin-left: -${spacing.medium};
27
+ `}
28
+ transition: ${misc.transition.default};
29
+ &:focus-within {
30
+ border-color: ${colors.brand.primary};
31
+ }
32
+ `;
33
+
34
+ export default TreeStructureWrapper;
@@ -0,0 +1,52 @@
1
+ import { FolderStructureProps } from './TreeStructure.types';
2
+
3
+ const getPathOfFolder = (data: FolderStructureProps[], findId: string): string[] => {
4
+ const paths = (dataChildren: FolderStructureProps[], path: string[]): string[] => {
5
+ for (const { id, data: dataChildrenSub } of dataChildren) {
6
+ if (id === findId) {
7
+ return [...path, id];
8
+ } else if (dataChildrenSub?.length) {
9
+ return paths(dataChildrenSub, [...path, id]);
10
+ }
11
+ }
12
+ return [];
13
+ };
14
+ return paths(data, []);
15
+ };
16
+
17
+ const getIdPathsOfFolder = (data: FolderStructureProps[], findId: string): number[] => {
18
+ let currentPath: number[] = [];
19
+ const paths = (dataChildren: FolderStructureProps[], path: number[]) => {
20
+ dataChildren.forEach(({ id, data: dataChildrenSub }, _index) => {
21
+ if (id === findId) {
22
+ currentPath = [...path, _index];
23
+ } else if (dataChildrenSub?.length) {
24
+ paths(dataChildrenSub, [...path, _index]);
25
+ }
26
+ });
27
+ };
28
+ paths(data, []);
29
+ return currentPath;
30
+ };
31
+
32
+ const getFolderName = (data: FolderStructureProps[], findId: string | undefined): string | undefined => {
33
+ if (!findId) {
34
+ return undefined;
35
+ }
36
+ let folderName: string | undefined;
37
+ const paths = (dataChildren: FolderStructureProps[]) => {
38
+ dataChildren.some(({ id, name, data: dataChildrenSub }, _index) => {
39
+ if (id === findId) {
40
+ folderName = name;
41
+ return true;
42
+ } else if (dataChildrenSub?.length) {
43
+ return paths(dataChildrenSub);
44
+ }
45
+ return false;
46
+ });
47
+ };
48
+ paths(data);
49
+ return folderName;
50
+ };
51
+
52
+ export { getPathOfFolder, getIdPathsOfFolder, getFolderName };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import TreeStructure from './TreeStructure';
10
+ export type { FolderStructureProps, TreeStructureProps } from './TreeStructure.types';
11
+ export { TreeStructure };
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import { FolderStructureProps, SetFocusedFolderId } from '../TreeStructure.types';
10
+ import { KeyboardNavigationProps, ElementWithKeyFocusProps } from './keyboardNavigation.types';
11
+ import { MAX_LEVEL_FOR_FOLDERS } from '../TreeStructure';
12
+
13
+ export const KEYBOARD_KEYS_OF_INTEREST = ['ArrowDown', 'ArrowUp', 'ArrowRight', 'ArrowLeft', ' '];
14
+
15
+ // Traverse upwards, incase parent is last element of its parent..
16
+ const traverseUpwards = (
17
+ inital: FolderStructureProps[],
18
+ setFocusedFolderId: SetFocusedFolderId,
19
+ paths: number[],
20
+ index: number,
21
+ ) => {
22
+ let findParent: FolderStructureProps[] = inital;
23
+ const parentNextIds: (string | false)[] = [];
24
+ paths.forEach((pathIndex) => {
25
+ const nextParent = findParent ? findParent[pathIndex + 1] : undefined;
26
+ parentNextIds.push(nextParent?.id || false);
27
+ findParent = findParent[pathIndex].data || [];
28
+ });
29
+ if (!parentNextIds.length) {
30
+ parentNextIds.push(findParent[index + 1]?.id || false);
31
+ }
32
+ // We use a reversed version of parentNextIds, filtered out falses, to find the next element
33
+ // No newId? We are at the end of the tree so we wont update.
34
+ const newId = parentNextIds.reverse().filter((id) => id)[0];
35
+ if (newId) {
36
+ setFocusedFolderId(newId);
37
+ }
38
+ };
39
+
40
+ const keyboardNavigation = ({
41
+ e,
42
+ data,
43
+ onToggleOpen,
44
+ setFocusedFolderId,
45
+ focusedFolderId: id,
46
+ openFolders,
47
+ }: KeyboardNavigationProps): string | undefined => {
48
+ if (e.key === ' ' && document.activeElement?.nodeName === 'INPUT') {
49
+ return;
50
+ }
51
+
52
+ // We are navigating in the tree.
53
+ // We need to find the next folder in the tree
54
+ const elementWithKeyFocus: ElementWithKeyFocusProps = {
55
+ paths: [],
56
+ index: 0,
57
+ };
58
+
59
+ const updatePathToElementWithKeyFocus = (
60
+ data: FolderStructureProps[],
61
+ paths: number[],
62
+ parent: FolderStructureProps[],
63
+ parentId?: string,
64
+ ): boolean =>
65
+ data.some(({ data: childData, id: dataId, url }, _index) => {
66
+ if (dataId === id) {
67
+ elementWithKeyFocus.paths = paths;
68
+ elementWithKeyFocus.index = _index;
69
+ elementWithKeyFocus.isOpen = openFolders.has(dataId) && childData && childData.length > 0;
70
+ elementWithKeyFocus.data = childData;
71
+ elementWithKeyFocus.parent = parent;
72
+ elementWithKeyFocus.parentId = parentId;
73
+ elementWithKeyFocus.url = url;
74
+ return true;
75
+ }
76
+ return childData ? updatePathToElementWithKeyFocus(childData, [...paths, _index], [...childData], dataId) : false;
77
+ });
78
+ if (!updatePathToElementWithKeyFocus(data, [], data)) {
79
+ // Couldn't find its location in the tree.
80
+ // This should not happen, reset its value to root.
81
+ setFocusedFolderId(e.key === 'ArrowDown' ? data[0].id : undefined);
82
+ return;
83
+ }
84
+ e.preventDefault();
85
+ e.stopPropagation();
86
+
87
+ if (e.key === ' ') {
88
+ const simulatedEvent = new MouseEvent('click', {
89
+ bubbles: true,
90
+ cancelable: true,
91
+ view: window,
92
+ });
93
+ document.activeElement && document.activeElement.dispatchEvent(simulatedEvent);
94
+ return;
95
+ }
96
+
97
+ if (e.key === 'ArrowRight') {
98
+ if (
99
+ !elementWithKeyFocus.isOpen &&
100
+ elementWithKeyFocus.data?.length &&
101
+ id &&
102
+ elementWithKeyFocus.paths.length < MAX_LEVEL_FOR_FOLDERS - 1
103
+ ) {
104
+ onToggleOpen(id);
105
+ }
106
+ return;
107
+ }
108
+ if (e.key === 'ArrowLeft') {
109
+ if (id && elementWithKeyFocus.isOpen) {
110
+ onToggleOpen(id);
111
+ }
112
+ return;
113
+ }
114
+
115
+ if (!id && e.key === 'ArrowDown') {
116
+ setFocusedFolderId(data[0].id);
117
+ return;
118
+ }
119
+ if (!id) {
120
+ return;
121
+ }
122
+ // Move up
123
+ if (e.key === 'ArrowUp') {
124
+ if (elementWithKeyFocus.index > 0) {
125
+ // Move upwards to the parent folder
126
+ setFocusedFolderId(
127
+ elementWithKeyFocus.parent ? elementWithKeyFocus.parent[elementWithKeyFocus.index - 1].id : undefined,
128
+ );
129
+ } else if (elementWithKeyFocus.paths.length > 0) {
130
+ elementWithKeyFocus.paths.pop();
131
+ let findParent = data;
132
+ elementWithKeyFocus.paths.forEach((index) => {
133
+ findParent = findParent[index].data as FolderStructureProps[];
134
+ });
135
+ const parentsCurrentIndex = findParent.findIndex(({ id }) => id === elementWithKeyFocus.parentId);
136
+ setFocusedFolderId(findParent[parentsCurrentIndex].id);
137
+ }
138
+ return;
139
+ }
140
+
141
+ if (elementWithKeyFocus.isOpen) {
142
+ if (elementWithKeyFocus.data?.length) {
143
+ setFocusedFolderId(elementWithKeyFocus.data[0].id);
144
+ } else {
145
+ // move to next child of parent if any... need new traverse :-/
146
+ traverseUpwards(data, setFocusedFolderId, elementWithKeyFocus.paths, elementWithKeyFocus.index);
147
+ }
148
+ return;
149
+ }
150
+
151
+ if (elementWithKeyFocus.parent && elementWithKeyFocus.index < elementWithKeyFocus.parent?.length - 1) {
152
+ // Move downwards to the next child
153
+ setFocusedFolderId(elementWithKeyFocus.parent[elementWithKeyFocus.index + 1].id);
154
+ return;
155
+ }
156
+
157
+ traverseUpwards(data, setFocusedFolderId, elementWithKeyFocus.paths, elementWithKeyFocus.index);
158
+ return;
159
+ };
160
+
161
+ export default keyboardNavigation;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Copyright (c) 2022-present, NDLA.
3
+ *
4
+ * This source code is licensed under the GPLv3 license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import { FolderStructureProps, SetFocusedFolderId } from '../TreeStructure.types';
10
+
11
+ export interface KeyboardNavigationProps {
12
+ e: React.KeyboardEvent<HTMLElement>;
13
+ data: FolderStructureProps[];
14
+ setFocusedFolderId: SetFocusedFolderId;
15
+ openFolders: Set<string>;
16
+ onToggleOpen: (id: string) => void;
17
+ focusedFolderId: string | undefined;
18
+ }
19
+
20
+ export interface ElementWithKeyFocusProps {
21
+ paths: number[];
22
+ index: number;
23
+ data?: FolderStructureProps[];
24
+ parent?: FolderStructureProps[];
25
+ parentId?: string;
26
+ isOpen?: boolean;
27
+ url?: string;
28
+ }
@@ -55,7 +55,7 @@ export interface FeideGoGroup extends FeideBaseGroup {
55
55
 
56
56
  export type FeideGroup = FeideOrg | FeideGoGroup;
57
57
 
58
- interface FeideUser {
58
+ export interface FeideUser {
59
59
  cn: string[];
60
60
  displayName: string;
61
61
  eduPersonAffiliation: AffiliationType[] | AffiliationType;
package/src/User/index.ts CHANGED
@@ -8,8 +8,8 @@
8
8
 
9
9
  import AuthModal from './AuthModal';
10
10
  import { UserInfo } from './UserInfo';
11
- import type { FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType } from './apiTypes';
11
+ import type { FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType, FeideMembershipType, FeideUser } from './apiTypes';
12
12
 
13
13
  export { UserInfo };
14
- export type { FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType };
14
+ export type { FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType, FeideMembershipType, FeideUser };
15
15
  export default AuthModal;
package/src/all.scss CHANGED
@@ -5,4 +5,5 @@
5
5
  @import '~@ndla/core/scss/utilities';
6
6
  @import '~@ndla/audio-search/scss/audio-search';
7
7
  @import '~@ndla/video-search/scss/video-search';
8
+ @import '~@ndla/button/src/all.scss';
8
9
  @import '~@ndla/tooltip/src/all.css';
package/src/index.ts CHANGED
@@ -22,6 +22,7 @@ export {
22
22
  ArticleHeaderWrapper,
23
23
  ArticleSideBar,
24
24
  default as Article,
25
+ ArticleFavoritesButton,
25
26
  } from './Article';
26
27
 
27
28
  export { default as Table } from './Table';
@@ -110,7 +111,7 @@ export { default as MastheadSearchModal } from './Masthead/MastheadSearchModal';
110
111
  export { default as MastheadAuthModal } from './Masthead/MastheadAuthModal';
111
112
  export { UserInfo } from './User';
112
113
  export { default as AuthModal } from './User';
113
- export type { FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType } from './User';
114
+ export type { FeideGoGroup, FeideGroup, FeideOrg, FeideUserApiType, FeideMembershipType, FeideUser } from './User';
114
115
 
115
116
  export { default as CreatedBy } from './CreatedBy';
116
117
 
@@ -153,7 +154,7 @@ export type { TopicProps } from './Topic';
153
154
  export { default as Aside } from './Aside';
154
155
  export { default as AuthorInfo } from './AuthorInfo';
155
156
 
156
- export { default as Breadcrumb, HeaderBreadcrumb, HomeBreadcrumb } from './Breadcrumb';
157
+ export { default as Breadcrumb, HeaderBreadcrumb, HomeBreadcrumb, ActionBreadcrumb } from './Breadcrumb';
157
158
  export type { SimpleBreadcrumbItem, IndexedBreadcrumbItem } from './Breadcrumb';
158
159
 
159
160
  export type { BreadcrumbItemProps } from './Breadcrumblist/Breadcrumblist';
@@ -221,7 +222,15 @@ export { default as CopyParagraphButton } from './CopyParagraphButton';
221
222
 
222
223
  export { default as ContentPlaceholder } from './ContentPlaceholder';
223
224
  export { Notion, ConceptNotion } from './Notion';
224
-
225
225
  export { BannerCard } from './BannerCard';
226
-
227
- export { ResourcesView, ResourceElement, Breadcrumbs } from './MyNdla';
226
+ export { VerticalNavigation, Folder, FolderInput } from './MyNdla';
227
+ export { ListResource, BlockResource } from './Resource';
228
+ export type { ListResourceProps } from './Resource';
229
+ export type { TagType } from './TagSelector';
230
+ export { TagSelector } from './TagSelector';
231
+
232
+ export type { SnackBarItem } from './SnackBar';
233
+ export { SnackBar } from './SnackBar';
234
+ export { InfoBlock } from './InfoBlock';
235
+ export { TreeStructure } from './TreeStructure';
236
+ export type { FolderStructureProps, TreeStructureProps } from './TreeStructure';
@@ -14,6 +14,19 @@ export const { contentTypes, subjectCategories, subjectTypes } = constants;
14
14
  const titleTemplate = ' - NDLA';
15
15
 
16
16
  const messages = {
17
+ treeStructure: {
18
+ createFolder: 'Create folder',
19
+ newFolder: {
20
+ placeholder: 'Add foldername',
21
+ defaultName: 'New folder',
22
+ },
23
+ },
24
+ tagSelector: {
25
+ placeholder: 'Add to tag',
26
+ removeTag: 'Remove tag {{name}}',
27
+ hideAllTags: 'Hide all tags',
28
+ showAllTags: 'Show all tags',
29
+ },
17
30
  htmlTitles: {
18
31
  titleTemplate,
19
32
  welcomePage: `Frontpage${titleTemplate}`,
@@ -965,15 +978,55 @@ const messages = {
965
978
  close: 'Close fact box',
966
979
  },
967
980
  myNdla: {
968
- resources: 'resources',
969
- folders: 'folders',
970
- folder: 'folder',
981
+ resources: '{{count}} Resource',
982
+ resources_plural: '{{count}} Resources',
983
+ folders: '{{count}} Folder',
984
+ folders_plural: '{{count}} Folders',
985
+ folder: 'Folder',
971
986
  myFolders: 'My folders',
972
987
  myTags: 'My tags',
973
988
  newFolder: 'New folder',
989
+ newFolderUnder: 'Create new folder under {{folderName}}',
974
990
  myAccount: 'My account',
975
991
  favourites: 'Favourites',
976
992
  help: 'Help',
993
+ more: 'More options',
994
+ listView: 'List view',
995
+ detailView: 'Detailed listview',
996
+ shortView: 'Short view',
997
+ myPage: {
998
+ myPage: 'My page',
999
+ logout: 'Log out of My NDLA',
1000
+ deleteAccount: 'Delete My NDLA',
1001
+ welcome:
1002
+ 'Welcome to my NDLA! You can now save your favourite resources from NDLA and organise them in folders with tags',
1003
+ read: { our: 'Read our', ours: 'Read our' },
1004
+ privacy: 'privacy statement',
1005
+ questions: { question: 'Any questions?', ask: 'Ask us in the chat' },
1006
+ wishToDelete: 'Do you wish to delete your account?',
1007
+ terms: 'terms of use',
1008
+ feide: 'We have retrieved this information from Feide',
1009
+ newFavourite: 'Recently favourited',
1010
+ resource: {
1011
+ addToMyNdla: 'Add to My NDLA',
1012
+ addedToMyNdla: 'Added to My NDLA',
1013
+ },
1014
+ storageInfo: {
1015
+ title: 'How to save your favourite resources from NDLA',
1016
+ text: 'When you wish to save a resource, you can do so by clicking the heart on the top right corner of the page. You will then get an option to store the resource in a folder',
1017
+ },
1018
+ folderInfo: {
1019
+ title: 'How to organise your favourite resources in folders',
1020
+ text: 'You can get to the folder overview by clicking on my folders on the menu to the left. Here you can create new folders and subfolder. You can also create a new folder in the dialogue window that is activated when you click on the heart in a resource',
1021
+ },
1022
+ tagInfo: {
1023
+ title: 'How to tag your favourite resources',
1024
+ text: 'When you save a resource, you will have the option to tag it with a keyword. This tag can be used to find the resource across folders. By selecting my tags on the menu to the left, you will see all the tags your have used. You can also see which resources are tagget with which keyword.',
1025
+ },
1026
+ },
1027
+ },
1028
+ snackbar: {
1029
+ close: 'Close notification',
977
1030
  },
978
1031
  labels: {
979
1032
  category: 'Category',
@@ -14,6 +14,19 @@ export const { contentTypes, subjectCategories, subjectTypes } = constants;
14
14
  const titleTemplate = ' - NDLA';
15
15
 
16
16
  const messages = {
17
+ treeStructure: {
18
+ createFolder: 'Lag mappe',
19
+ newFolder: {
20
+ placeholder: 'Skriv navn på mappe',
21
+ defaultName: 'Ny mappe',
22
+ },
23
+ },
24
+ tagSelector: {
25
+ placeholder: 'Tilknytt tag',
26
+ removeTag: 'Ta vekk tilknytning til {{name}}',
27
+ hideAllTags: 'Skjul alle tagger',
28
+ showAllTags: 'Vis alle tagger',
29
+ },
17
30
  htmlTitles: {
18
31
  titleTemplate,
19
32
  welcomePage: `Forsiden${titleTemplate}`,
@@ -963,15 +976,55 @@ const messages = {
963
976
  close: 'Lukk faktaboks',
964
977
  },
965
978
  myNdla: {
966
- resources: 'Ressurser',
967
- folders: 'Mapper',
979
+ resources: '{{count}} ressurs',
980
+ resources_plural: '{{count}} ressurser',
981
+ folders: '{{count}} mappe',
982
+ folders_plural: '{{count}} mapper',
968
983
  folder: 'Mappe',
969
984
  myFolders: 'Mine mapper',
970
985
  myTags: 'Mine tags',
971
986
  newFolder: 'Ny mappe',
987
+ newFolderUnder: 'Lag ny mappe under {{folderName}}',
972
988
  myAccount: 'Min konto',
973
989
  favourites: 'Favoritter',
974
990
  help: 'Hjelp',
991
+ more: 'Flere valg',
992
+ listView: 'Listevisning',
993
+ detailView: 'Detaljrik listevisning',
994
+ shortView: 'Kort visning',
995
+ myPage: {
996
+ myPage: 'Min side',
997
+ deleteAccount: 'Slett Min NDLA',
998
+ logout: 'Logg ut av Min NDLA',
999
+ welcome:
1000
+ 'Velkommen til Min NDLA! Nå kan du lagre dine favorittressurser fra NDLA og organisere dem slik du ønsker i mapper og med tags.',
1001
+ read: { our: 'Les våre', ours: 'Les vår' },
1002
+ privacy: 'personvernerklæring her',
1003
+ questions: { question: 'Lurer du på noe?', ask: 'Spør oss i chatten' },
1004
+ wishToDelete: 'Ønsker du ikke ha brukerprofil hos oss lengre?',
1005
+ terms: 'vilkår for bruk',
1006
+ newFavourite: 'Nylig lagt til',
1007
+ feide: 'Dette henter vi om deg fra Feide',
1008
+ resource: {
1009
+ addToMyNdla: 'Legg i Min NDLA',
1010
+ addedToMyNdla: 'Lagt i Min NDLA',
1011
+ },
1012
+ storageInfo: {
1013
+ title: 'Slik lagrer du dine favorittressurser fra NDLA',
1014
+ text: 'Når du ønsker å lagre en ressurs, kan du gjøre dette ved å klikke på hjertet øverst til høyre på siden. Du vil da få mulighet til å lagre ressursen i en mappe.',
1015
+ },
1016
+ folderInfo: {
1017
+ title: 'Slik organiserer du dine favorittressurser i mapper',
1018
+ text: 'Du kommer til mappeoversikten din ved å klikke på mine mapper i menyen til venstre. Her kan du opprette nye mapper og undermapper. Du kan også opprette en ny mappe i dialogvinduet som kommer når du klikker på et hjerte i en ressurs.',
1019
+ },
1020
+ tagInfo: {
1021
+ title: 'Slik tagger du dine favorittressurser',
1022
+ text: 'Når du lagrer en ressurs får du mulighet til å tagge ressursen med et nøkkelord. Du kan senere bruke taggene til å finne tilbake til ressurser på tvers av mapper. Ved å velge mine tagger i venstremenyen får du oversikt over alle taggene du har brukt og du kan også her se hvilke ressurser som du har tagget med det bestemte nøkkelordet.',
1023
+ },
1024
+ },
1025
+ },
1026
+ snackbar: {
1027
+ close: 'Lukk melding',
975
1028
  },
976
1029
  labels: {
977
1030
  category: 'Kategori',
@@ -14,6 +14,19 @@ export const { contentTypes, subjectCategories, subjectTypes } = constants;
14
14
  const titleTemplate = ' - NDLA';
15
15
 
16
16
  const messages = {
17
+ treeStructure: {
18
+ createFolder: 'Lag mappe',
19
+ newFolder: {
20
+ placeholder: 'Skriv navn på mappe',
21
+ defaultName: 'Ny mappe',
22
+ },
23
+ },
24
+ tagSelector: {
25
+ placeholder: 'Tilknytt tag',
26
+ removeTag: 'Ta vekk tilknytninga til {{name}}',
27
+ hideAllTags: 'Skjul alle tagger',
28
+ showAllTags: 'Vis alle tagger',
29
+ },
17
30
  htmlTitles: {
18
31
  titleTemplate,
19
32
  welcomePage: `Framsida${titleTemplate}`,
@@ -964,15 +977,55 @@ const messages = {
964
977
  close: 'Lukk faktaboks',
965
978
  },
966
979
  myNdla: {
967
- resources: 'Ressursar',
968
- folders: 'Mapper',
980
+ resources: '{{count}} ressurs',
981
+ resources_plural: '{{count}} ressursar',
982
+ folders: '{{count}} mappe',
983
+ folders_plural: '{{count}} mapper',
969
984
  folder: 'Mappe',
970
985
  myFolders: 'Mine mapper',
971
986
  myTags: 'Mine tags',
972
987
  newFolder: 'Ny mappe',
988
+ newFolderUnder: 'Lag ny mappe under {{folderName}}',
973
989
  myAccount: 'Min konto',
974
990
  favourites: 'Favorittar',
975
991
  help: 'Hjelp',
992
+ more: 'Fleire val',
993
+ listView: 'Listevisning',
994
+ detailView: 'Detaljrik listevisning',
995
+ shortView: 'Kort visning',
996
+ myPage: {
997
+ myPage: 'Min side',
998
+ deleteAccount: 'Slett Min NDLA',
999
+ logout: 'Logg ut av Min NDLA',
1000
+ welcome:
1001
+ 'Velkommen til Min NDLA! Nå kan du lagre dine favorittressurser fra NDLA og organisere dem slik du ønsker i mapper og med tags.',
1002
+ read: { our: 'Les våre', ours: 'Les vår' },
1003
+ privacy: 'personvernerklæring her',
1004
+ questions: { question: 'Lurer du på noe?', ask: 'Spør oss i chatten' },
1005
+ wishToDelete: 'Ønsker du ikke ha brukerprofil hos oss lengre?',
1006
+ terms: 'vilkår for bruk',
1007
+ newFavourite: 'Nylig lagt til',
1008
+ feide: 'Dette henter vi om deg fra Feide',
1009
+ resource: {
1010
+ addToMyNdla: 'Legg i Min NDLA',
1011
+ addedToMyNdla: 'Lagt i Min NDLA',
1012
+ },
1013
+ storageInfo: {
1014
+ title: 'Slik lagrer du dine favorittressurser fra NDLA',
1015
+ text: 'Når du ønsker å lagre en ressurs, kan du gjøre dette ved å klikke på hjertet øverst til høyre på siden. Du vil da få mulighet til å lagre ressursen i en mappe.',
1016
+ },
1017
+ folderInfo: {
1018
+ title: 'Slik organiserer du dine favorittressurser i mapper',
1019
+ text: 'Du kommer til mappeoversikten din ved å klikke på mine mapper i menyen til venstre. Her kan du opprette nye mapper og undermapper. Du kan også opprette en ny mappe i dialogvinduet som kommer når du klikker på et hjerte i en ressurs.',
1020
+ },
1021
+ tagInfo: {
1022
+ title: 'Slik tagger du dine favorittressurser',
1023
+ text: 'Når du lagrer en ressurs får du mulighet til å tagge ressursen med et nøkkelord. Du kan senere bruke taggene til å finne tilbake til ressurser på tvers av mapper. Ved å velge mine tagger i venstremenyen får du oversikt over alle taggene du har brukt og du kan også her se hvilke ressurser som du har tagget med det bestemte nøkkelordet.',
1024
+ },
1025
+ },
1026
+ },
1027
+ snackbar: {
1028
+ close: 'Lukk melding',
976
1029
  },
977
1030
  labels: {
978
1031
  category: 'Kategori',