@ndla/ui 16.1.1 → 18.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/ArticleFavoritesButton.js +4 -3
- package/es/Breadcrumblist/Breadcrumblist.js +7 -7
- package/es/Frontpage/FrontpageAllSubjects.js +8 -8
- package/es/Masthead/Masthead.js +2 -3
- package/es/Messages/MessageBanner.js +75 -0
- package/es/Messages/MessageBox.js +123 -0
- package/es/{MessageBox → Messages}/MessageBoxTag.js +1 -1
- package/es/{MessageBox → Messages}/index.js +3 -3
- package/es/MyNdla/Resource/Folder.js +6 -6
- package/es/MyNdla/index.js +1 -2
- package/es/Notion/ConceptNotion.js +1 -1
- package/es/Notion/index.js +2 -1
- package/es/Programme/Programme.js +6 -6
- package/es/Programme/ProgrammeSubjects.js +2 -2
- package/es/Resource/ListResource.js +6 -6
- package/es/TagSelector/SuggestionInput.js +111 -56
- package/es/TagSelector/Suggestions.js +19 -15
- package/es/TagSelector/TagSelector.js +8 -7
- package/es/Topic/Topic.js +22 -24
- package/es/TopicMenu/TopicMenu.js +2 -4
- package/es/TreeStructure/FolderItem.js +39 -28
- package/es/TreeStructure/FolderItems.js +8 -5
- package/es/TreeStructure/TreeStructure.js +6 -4
- package/es/TreeStructure/TreeStructureWrapper.js +2 -2
- package/es/index.js +3 -3
- package/es/locale/messages-en.js +9 -2
- package/es/locale/messages-nb.js +10 -3
- package/es/locale/messages-nn.js +11 -4
- package/es/locale/messages-se.js +10 -3
- package/es/locale/messages-sma.js +10 -3
- package/lib/Article/Article.js +3 -3
- package/lib/Article/ArticleFavoritesButton.js +4 -3
- package/lib/Breadcrumblist/Breadcrumblist.js +7 -7
- package/lib/Breadcrumblist/index.d.ts +1 -0
- package/lib/Frontpage/FrontpageAllSubjects.js +9 -9
- package/lib/Masthead/Masthead.js +2 -3
- package/lib/Messages/MessageBanner.d.ts +16 -0
- package/lib/Messages/MessageBanner.js +78 -0
- package/lib/{MessageBox → Messages}/MessageBox.d.ts +6 -14
- package/lib/Messages/MessageBox.js +128 -0
- package/lib/{MessageBox → Messages}/MessageBoxTag.d.ts +0 -0
- package/lib/{MessageBox → Messages}/MessageBoxTag.js +1 -1
- package/lib/{MessageBox → Messages}/index.d.ts +3 -3
- package/lib/Messages/index.js +31 -0
- package/lib/MyNdla/Resource/Folder.js +6 -6
- package/lib/MyNdla/index.d.ts +1 -2
- package/lib/MyNdla/index.js +0 -8
- package/lib/Notion/ConceptNotion.d.ts +1 -1
- package/lib/Notion/ConceptNotion.js +1 -1
- package/lib/Notion/index.d.ts +4 -1
- package/lib/Notion/index.js +11 -3
- package/lib/Programme/Programme.js +6 -6
- package/lib/Programme/ProgrammeSubjects.js +3 -3
- package/lib/Resource/ListResource.js +6 -6
- package/lib/TagSelector/SuggestionInput.js +111 -57
- package/lib/TagSelector/Suggestions.js +26 -23
- package/lib/TagSelector/TagSelector.js +8 -7
- package/lib/Topic/Topic.js +22 -24
- package/lib/TopicMenu/TopicMenu.js +2 -4
- package/lib/TreeStructure/FolderItem.d.ts +3 -2
- package/lib/TreeStructure/FolderItem.js +38 -28
- package/lib/TreeStructure/FolderItems.d.ts +1 -1
- package/lib/TreeStructure/FolderItems.js +8 -5
- package/lib/TreeStructure/TreeStructure.d.ts +1 -1
- package/lib/TreeStructure/TreeStructure.js +6 -4
- package/lib/TreeStructure/TreeStructure.types.d.ts +6 -3
- package/lib/TreeStructure/TreeStructureWrapper.js +2 -2
- package/lib/index.d.ts +8 -4
- package/lib/index.js +13 -13
- package/lib/locale/messages-en.d.ts +7 -0
- package/lib/locale/messages-en.js +9 -2
- package/lib/locale/messages-nb.d.ts +7 -0
- package/lib/locale/messages-nb.js +10 -3
- package/lib/locale/messages-nn.d.ts +7 -0
- package/lib/locale/messages-nn.js +11 -4
- package/lib/locale/messages-se.d.ts +7 -0
- package/lib/locale/messages-se.js +10 -3
- package/lib/locale/messages-sma.d.ts +7 -0
- package/lib/locale/messages-sma.js +10 -3
- package/package.json +13 -13
- package/src/Article/Article.tsx +1 -1
- package/src/Article/ArticleFavoritesButton.tsx +4 -3
- package/src/Breadcrumblist/Breadcrumblist.tsx +1 -1
- package/src/Breadcrumblist/{index.tsx → index.ts} +1 -0
- package/src/Frontpage/FrontpageAllSubjects.tsx +1 -1
- package/src/Masthead/Masthead.tsx +3 -6
- package/src/Messages/MessageBanner.tsx +66 -0
- package/src/Messages/MessageBox.tsx +156 -0
- package/src/{MessageBox → Messages}/MessageBoxTag.tsx +0 -0
- package/src/{MessageBox → Messages}/index.ts +3 -3
- package/src/MyNdla/Resource/Folder.tsx +1 -1
- package/src/MyNdla/index.ts +1 -2
- package/src/Notion/ConceptNotion.tsx +2 -1
- package/src/Notion/index.ts +4 -1
- package/src/Programme/Programme.tsx +1 -1
- package/src/Programme/ProgrammeSubjects.tsx +1 -1
- package/src/Resource/ListResource.tsx +1 -1
- package/src/TagSelector/SuggestionInput.tsx +90 -24
- package/src/TagSelector/Suggestions.tsx +14 -0
- package/src/TagSelector/TagSelector.tsx +6 -4
- package/src/Topic/Topic.tsx +2 -2
- package/src/TopicMenu/TopicMenu.jsx +2 -2
- package/src/TreeStructure/FolderItem.tsx +43 -19
- package/src/TreeStructure/FolderItems.tsx +3 -0
- package/src/TreeStructure/TreeStructure.tsx +2 -0
- package/src/TreeStructure/TreeStructure.types.ts +7 -3
- package/src/TreeStructure/TreeStructureWrapper.tsx +1 -1
- package/src/index.ts +17 -4
- package/src/locale/messages-en.ts +10 -2
- package/src/locale/messages-nb.ts +10 -3
- package/src/locale/messages-nn.ts +11 -4
- package/src/locale/messages-se.ts +10 -3
- package/src/locale/messages-sma.ts +10 -3
- package/es/MessageBox/MessageBox.js +0 -220
- package/es/MyNdla/Navigation/VerticalNavigation.js +0 -51
- package/es/MyNdla/Navigation/index.js +0 -2
- package/lib/MessageBox/MessageBox.js +0 -234
- package/lib/MessageBox/index.js +0 -35
- package/lib/MyNdla/Navigation/VerticalNavigation.d.ts +0 -10
- package/lib/MyNdla/Navigation/VerticalNavigation.js +0 -61
- package/lib/MyNdla/Navigation/index.d.ts +0 -2
- package/lib/MyNdla/Navigation/index.js +0 -15
- package/src/MessageBox/MessageBox.tsx +0 -201
- package/src/MyNdla/Navigation/VerticalNavigation.tsx +0 -93
- package/src/MyNdla/Navigation/index.ts +0 -2
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
import styled from '@emotion/styled';
|
|
11
|
+
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
12
|
+
import { InformationOutline, HumanMaleBoard, Forward, WarningOutline } from '@ndla/icons/common';
|
|
13
|
+
import { WithTranslation, withTranslation } from 'react-i18next';
|
|
14
|
+
|
|
15
|
+
// @ts-ignore
|
|
16
|
+
import { Remarkable } from 'remarkable';
|
|
17
|
+
import { CloseButton } from '@ndla/button';
|
|
18
|
+
import { css } from '@emotion/core';
|
|
19
|
+
|
|
20
|
+
const markdown = new Remarkable({ breaks: true });
|
|
21
|
+
markdown.inline.ruler.enable(['sub', 'sup']);
|
|
22
|
+
markdown.block.ruler.disable(['list', 'table']);
|
|
23
|
+
|
|
24
|
+
type MessageBoxType = 'ghost' | 'danger';
|
|
25
|
+
|
|
26
|
+
interface StyledProps {
|
|
27
|
+
type?: MessageBoxType;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const MessageBoxWrapper = styled.div<StyledProps>`
|
|
31
|
+
display: flex;
|
|
32
|
+
padding: ${spacing.small};
|
|
33
|
+
font-family: ${fonts.sans};
|
|
34
|
+
border-radius: 5px;
|
|
35
|
+
background: ${colors.support.yellowLight};
|
|
36
|
+
color: ${colors.brand.greyDark};
|
|
37
|
+
|
|
38
|
+
${fonts.sizes('18px')};
|
|
39
|
+
${mq.range({ until: breakpoints.tabletWide })} {
|
|
40
|
+
${fonts.sizes('16px')};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
${({ type }) =>
|
|
44
|
+
type === 'ghost' &&
|
|
45
|
+
css`
|
|
46
|
+
background: transparent;
|
|
47
|
+
border: 1px solid ${colors.brand.neutral7};
|
|
48
|
+
color: ${colors.brand.greyDark};
|
|
49
|
+
`}
|
|
50
|
+
|
|
51
|
+
${({ type }) =>
|
|
52
|
+
type === 'danger' &&
|
|
53
|
+
css`
|
|
54
|
+
background: ${colors.support.redLightest};
|
|
55
|
+
color: ${colors.support.red};
|
|
56
|
+
`}
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
const InfoWrapper = styled.div`
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: row;
|
|
62
|
+
flex: 1;
|
|
63
|
+
padding: ${spacing.small};
|
|
64
|
+
padding-right: 0;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const TextWrapper = styled.div`
|
|
68
|
+
& p {
|
|
69
|
+
margin: 0;
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const IconWrapper = styled.div`
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: flex-start;
|
|
76
|
+
padding-right: ${spacing.small};
|
|
77
|
+
|
|
78
|
+
svg {
|
|
79
|
+
width: 24px;
|
|
80
|
+
height: 24px;
|
|
81
|
+
}
|
|
82
|
+
`;
|
|
83
|
+
|
|
84
|
+
const LinkWrapper = styled.div`
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-wrap: wrap;
|
|
87
|
+
gap: ${spacing.normal};
|
|
88
|
+
padding-top: ${spacing.nsmall};
|
|
89
|
+
|
|
90
|
+
svg {
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
}
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const Link = styled.a`
|
|
96
|
+
display: flex;
|
|
97
|
+
align-items: center;
|
|
98
|
+
color: ${colors.brand.primary};
|
|
99
|
+
gap: ${spacing.xxsmall};
|
|
100
|
+
font-weight: ${fonts.weight.semibold};
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const StyledClosebutton = styled(CloseButton)`
|
|
104
|
+
padding: 0;
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
interface LinkProps {
|
|
108
|
+
href?: string;
|
|
109
|
+
text?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
interface Props {
|
|
113
|
+
type?: MessageBoxType;
|
|
114
|
+
children?: string;
|
|
115
|
+
links?: LinkProps[];
|
|
116
|
+
showCloseButton?: boolean;
|
|
117
|
+
onClose?: () => void;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const Icon = ({ type }: StyledProps) => {
|
|
121
|
+
if (type === 'ghost') {
|
|
122
|
+
return <HumanMaleBoard />;
|
|
123
|
+
}
|
|
124
|
+
if (type === 'danger') {
|
|
125
|
+
return <WarningOutline />;
|
|
126
|
+
}
|
|
127
|
+
return <InformationOutline />;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const MessageBox = ({ type, children = '', links, showCloseButton, onClose }: Props & WithTranslation) => {
|
|
131
|
+
return (
|
|
132
|
+
<MessageBoxWrapper type={type}>
|
|
133
|
+
<InfoWrapper>
|
|
134
|
+
<IconWrapper>
|
|
135
|
+
<Icon type={type} />
|
|
136
|
+
</IconWrapper>
|
|
137
|
+
<div>
|
|
138
|
+
<TextWrapper dangerouslySetInnerHTML={{ __html: markdown.render(children) }} />
|
|
139
|
+
{links && (
|
|
140
|
+
<LinkWrapper>
|
|
141
|
+
{links.map((x) => (
|
|
142
|
+
<Link href={x.href}>
|
|
143
|
+
<span>{x.text}</span>
|
|
144
|
+
<Forward />
|
|
145
|
+
</Link>
|
|
146
|
+
))}
|
|
147
|
+
</LinkWrapper>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
</InfoWrapper>
|
|
151
|
+
{showCloseButton && <StyledClosebutton onClick={onClose} />}
|
|
152
|
+
</MessageBoxWrapper>
|
|
153
|
+
);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export default withTranslation()(MessageBox);
|
|
File without changes
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import MessageBox from './MessageBox';
|
|
10
9
|
import MessageBoxTag from './MessageBoxTag';
|
|
11
|
-
import
|
|
10
|
+
import MessageBox from './MessageBox';
|
|
11
|
+
import MessageBanner from './MessageBanner';
|
|
12
12
|
|
|
13
|
-
export { MessageBox, MessageBoxTag,
|
|
13
|
+
export { MessageBox, MessageBoxTag, MessageBanner };
|
|
@@ -59,7 +59,7 @@ const FolderWrapper = styled(SafeLink)`
|
|
|
59
59
|
display: flex;
|
|
60
60
|
align-items: center;
|
|
61
61
|
padding: ${spacing.small};
|
|
62
|
-
border: 1px solid ${colors.brand.
|
|
62
|
+
border: 1px solid ${colors.brand.neutral7};
|
|
63
63
|
border-radius: 2px;
|
|
64
64
|
box-shadow: none;
|
|
65
65
|
text-decoration: none;
|
package/src/MyNdla/index.ts
CHANGED
|
@@ -16,7 +16,8 @@ import { getLicenseCredits } from '@ndla/licenses';
|
|
|
16
16
|
import Notion, { NotionDialogContent, NotionDialogText, NotionDialogLicenses } from '@ndla/notion';
|
|
17
17
|
import { Notion as UINotion } from '.';
|
|
18
18
|
import { NotionImage } from './NotionImage';
|
|
19
|
-
import NotionVisualElement
|
|
19
|
+
import NotionVisualElement from './NotionVisualElement';
|
|
20
|
+
import type { NotionVisualElementType } from './NotionVisualElement';
|
|
20
21
|
import FigureNotion from './FigureNotion';
|
|
21
22
|
import { Copyright } from '../types';
|
|
22
23
|
import { FigureType } from '../Figure';
|
package/src/Notion/index.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
export { default as Notion } from './Notion';
|
|
2
1
|
export { default as ConceptNotion } from './ConceptNotion';
|
|
2
|
+
export { default as Notion } from './Notion';
|
|
3
|
+
export { default as NotionVisualElement } from './NotionVisualElement';
|
|
4
|
+
export type { NotionVisualElementType } from './NotionVisualElement';
|
|
5
|
+
export type { ConceptNotionType } from './ConceptNotion';
|
|
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
import LayoutItem, { OneColumn } from '../Layout';
|
|
6
6
|
import ProgrammeSubjects from './ProgrammeSubjects';
|
|
7
7
|
import { GradesProps } from './ProgrammeSubjects';
|
|
8
|
-
import MessageBox from '../
|
|
8
|
+
import MessageBox from '../Messages/MessageBox';
|
|
9
9
|
import { NavigationHeading } from '..';
|
|
10
10
|
const StyledWrapper = styled.div`
|
|
11
11
|
display: flex;
|
|
@@ -12,7 +12,7 @@ import styled from '@emotion/styled';
|
|
|
12
12
|
import Button from '@ndla/button';
|
|
13
13
|
import { breakpoints, mq } from '@ndla/core';
|
|
14
14
|
import { NavigationBox } from '../Navigation';
|
|
15
|
-
import { MessageBox } from '../
|
|
15
|
+
import { MessageBox } from '../Messages';
|
|
16
16
|
|
|
17
17
|
const GradesMenu = styled.div`
|
|
18
18
|
margin-bottom: 28px;
|
|
@@ -47,7 +47,7 @@ const ResourceWrapper = styled(SafeLink)`
|
|
|
47
47
|
text-decoration: none;
|
|
48
48
|
box-shadow: none;
|
|
49
49
|
padding: ${spacing.small};
|
|
50
|
-
border: 1px solid ${colors.brand.
|
|
50
|
+
border: 1px solid ${colors.brand.neutral7};
|
|
51
51
|
border-radius: 2px;
|
|
52
52
|
color: ${colors.brand.greyDark};
|
|
53
53
|
gap: 0 ${spacing.small};
|
|
@@ -19,6 +19,39 @@ import { uuid } from '@ndla/util';
|
|
|
19
19
|
import Suggestions from './Suggestions';
|
|
20
20
|
import type { TagType } from './TagSelector';
|
|
21
21
|
|
|
22
|
+
const SuggestionTextWrapper = styled.div`
|
|
23
|
+
${fonts.sizes(18)};
|
|
24
|
+
position: absolute;
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-grow: 1;
|
|
27
|
+
left: 0;
|
|
28
|
+
right: 0;
|
|
29
|
+
overflow: hidden;
|
|
30
|
+
max-height: ${spacing.large};
|
|
31
|
+
padding: 8.333px;
|
|
32
|
+
padding-right: ${spacing.large};
|
|
33
|
+
span {
|
|
34
|
+
color: ${colors.brand.grey};
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
overflow: hidden !important;
|
|
37
|
+
text-overflow: ellipsis;
|
|
38
|
+
&:first-of-type {
|
|
39
|
+
color: transparent;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const SuggestionText = ({ value, suggestionValue }: { value: string; suggestionValue: string }) => (
|
|
45
|
+
<SuggestionTextWrapper>
|
|
46
|
+
{!!value && (
|
|
47
|
+
<>
|
|
48
|
+
<span>{value}</span>
|
|
49
|
+
<span>{suggestionValue.substring(value.length)}</span>
|
|
50
|
+
</>
|
|
51
|
+
)}
|
|
52
|
+
</SuggestionTextWrapper>
|
|
53
|
+
);
|
|
54
|
+
|
|
22
55
|
const Cross = styled(CrossRaw)`
|
|
23
56
|
margin-left: ${spacing.xxsmall};
|
|
24
57
|
`;
|
|
@@ -31,15 +64,18 @@ const StyledInput = styled.input`
|
|
|
31
64
|
flex-grow: 1;
|
|
32
65
|
border: 0;
|
|
33
66
|
outline: none;
|
|
34
|
-
background: transparent;
|
|
35
67
|
${fonts.sizes(18)};
|
|
68
|
+
z-index: 1;
|
|
69
|
+
position: relative;
|
|
70
|
+
background: transparent;
|
|
36
71
|
`;
|
|
72
|
+
|
|
37
73
|
const StyledInputWrapper = styled.div`
|
|
38
74
|
display: flex;
|
|
39
75
|
flex-wrap: wrap;
|
|
40
76
|
gap: ${spacing.xsmall};
|
|
41
77
|
padding: ${spacing.small};
|
|
42
|
-
border: 1px solid ${colors.brand.
|
|
78
|
+
border: 1px solid ${colors.brand.neutral7};
|
|
43
79
|
transition: border-color ${animations.durations.normal} ease;
|
|
44
80
|
border-radius: ${misc.borderRadius};
|
|
45
81
|
&:focus-within {
|
|
@@ -50,6 +86,12 @@ const StyledInputWrapper = styled.div`
|
|
|
50
86
|
const CombinedInputAndDropdownWrapper = styled.div`
|
|
51
87
|
display: flex;
|
|
52
88
|
flex-grow: 1;
|
|
89
|
+
position: relative;
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const StyledTagButton = styled(Button)<{ enableTagButtonAnimation: boolean }>`
|
|
93
|
+
${({ enableTagButtonAnimation }) =>
|
|
94
|
+
enableTagButtonAnimation ? animations.fadeInScaled(animations.durations.slow) : ''}
|
|
53
95
|
`;
|
|
54
96
|
|
|
55
97
|
interface SuggestionInputProps {
|
|
@@ -90,6 +132,7 @@ const SuggestionInput = ({
|
|
|
90
132
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
91
133
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
92
134
|
const suggestionIdRef = useRef<string>(uuid());
|
|
135
|
+
const initalTags = useRef<string[]>(addedTags.map(({ id }) => id));
|
|
93
136
|
|
|
94
137
|
useEffect(() => {
|
|
95
138
|
setCurrentHighlightedIndex(0);
|
|
@@ -122,7 +165,8 @@ const SuggestionInput = ({
|
|
|
122
165
|
<SuggestionInputContainer ref={containerRef}>
|
|
123
166
|
<StyledInputWrapper>
|
|
124
167
|
{addedTags.map(({ id, name }) => (
|
|
125
|
-
<
|
|
168
|
+
<StyledTagButton
|
|
169
|
+
enableTagButtonAnimation={!initalTags.current.includes(id)}
|
|
126
170
|
aria-label={t('tagSelector.removeTag', { name })}
|
|
127
171
|
onClick={() => onToggleTag(id)}
|
|
128
172
|
light
|
|
@@ -132,9 +176,12 @@ const SuggestionInput = ({
|
|
|
132
176
|
{prefix}
|
|
133
177
|
{name}
|
|
134
178
|
<Cross />
|
|
135
|
-
</
|
|
179
|
+
</StyledTagButton>
|
|
136
180
|
))}
|
|
137
181
|
<CombinedInputAndDropdownWrapper>
|
|
182
|
+
{suggestions[currentHighlightedIndex] && (
|
|
183
|
+
<SuggestionText value={value} suggestionValue={suggestions[currentHighlightedIndex].name} />
|
|
184
|
+
)}
|
|
138
185
|
<StyledInput
|
|
139
186
|
placeholder={t('tagSelector.placeholder')}
|
|
140
187
|
value={value}
|
|
@@ -157,38 +204,57 @@ const SuggestionInput = ({
|
|
|
157
204
|
}}
|
|
158
205
|
ref={inputRef}
|
|
159
206
|
onKeyDown={(e: KeyboardEvent<HTMLInputElement>) => {
|
|
207
|
+
if (!['Enter', ' ', 'Tab', 'ArrowDown', 'ArrowUp', 'Backspace'].includes(e.key)) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const trimmedValue = value.replace(/\s/g, '');
|
|
160
211
|
if (e.key === 'Escape') {
|
|
161
212
|
setExpanded(false);
|
|
162
213
|
e.preventDefault();
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
e.preventDefault();
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
onCreateTag(value);
|
|
175
|
-
setInputValue('');
|
|
176
|
-
e.preventDefault();
|
|
177
|
-
}
|
|
178
|
-
} else if (e.key === 'Enter') {
|
|
179
|
-
e.preventDefault();
|
|
180
|
-
}
|
|
181
|
-
} else if (e.key === 'ArrowUp') {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (e.key === 'Backspace' && trimmedValue === '' && addedTags.length) {
|
|
217
|
+
// Remove the added last tag
|
|
218
|
+
onToggleTag(addedTags[addedTags.length - 1].id);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (e.key === 'ArrowUp') {
|
|
182
222
|
setCurrentHighlightedIndex(
|
|
183
223
|
currentHighlightedIndex - 1 < 0 ? suggestions.length - 1 : currentHighlightedIndex - 1,
|
|
184
224
|
);
|
|
185
225
|
e.preventDefault();
|
|
186
|
-
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (e.key === 'ArrowDown') {
|
|
187
229
|
setCurrentHighlightedIndex(
|
|
188
230
|
currentHighlightedIndex + 1 >= suggestions.length ? 0 : currentHighlightedIndex + 1,
|
|
189
231
|
);
|
|
190
232
|
e.preventDefault();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (trimmedValue === '' && !expanded) {
|
|
236
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
237
|
+
e.preventDefault();
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
if (e.key === 'Enter' || e.key === 'Tab' || e.key === ' ') {
|
|
242
|
+
if (suggestions.length > 0) {
|
|
243
|
+
if (!hasBeenAdded(suggestions[currentHighlightedIndex].id)) {
|
|
244
|
+
onToggleTag(suggestions[currentHighlightedIndex].id);
|
|
245
|
+
} else if (trimmedValue.length < suggestions[currentHighlightedIndex].name.length) {
|
|
246
|
+
onCreateTag(trimmedValue);
|
|
247
|
+
e.preventDefault();
|
|
248
|
+
}
|
|
249
|
+
setInputValue('');
|
|
250
|
+
e.preventDefault();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
onCreateTag(trimmedValue);
|
|
254
|
+
setInputValue('');
|
|
255
|
+
e.preventDefault();
|
|
191
256
|
}
|
|
257
|
+
return;
|
|
192
258
|
}}
|
|
193
259
|
/>
|
|
194
260
|
<Tooltip tooltip={expanded ? t('tagSelector.hideAllTags') : t('tagSelector.showAllTags')}>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import styled from '@emotion/styled';
|
|
11
|
+
import { css } from '@emotion/core';
|
|
11
12
|
import { Check } from '@ndla/icons/editor';
|
|
12
13
|
import { spacing, colors, misc, animations, fonts, shadows } from '@ndla/core';
|
|
13
14
|
import Button from '@ndla/button';
|
|
@@ -60,6 +61,7 @@ interface SuggestionButtonProps {
|
|
|
60
61
|
const SuggestionButton = styled(Button)<SuggestionButtonProps>`
|
|
61
62
|
display: flex;
|
|
62
63
|
justify-content: space-between;
|
|
64
|
+
align-items: center;
|
|
63
65
|
${fonts.sizes(18)};
|
|
64
66
|
transition: ${misc.transition.default};
|
|
65
67
|
font-weight: 400;
|
|
@@ -72,6 +74,18 @@ const SuggestionButton = styled(Button)<SuggestionButtonProps>`
|
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
}
|
|
77
|
+
${({ isHighlighted }) =>
|
|
78
|
+
isHighlighted
|
|
79
|
+
? css`
|
|
80
|
+
background: ${colors.brand.lighter};
|
|
81
|
+
&:disabled {
|
|
82
|
+
background: ${colors.brand.greyLighter};
|
|
83
|
+
svg {
|
|
84
|
+
fill: ${colors.brand.grey};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
: ''}
|
|
75
89
|
`;
|
|
76
90
|
|
|
77
91
|
interface Props {
|
|
@@ -33,10 +33,12 @@ interface Props {
|
|
|
33
33
|
prefix?: string;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const sortedTags = (tags: TagType[], selectedTags: string[]): TagType[] =>
|
|
37
|
-
|
|
38
|
-
.
|
|
39
|
-
.
|
|
36
|
+
const sortedTags = (tags: TagType[], selectedTags: string[]): TagType[] => {
|
|
37
|
+
const returnTags = selectedTags
|
|
38
|
+
.map((selectedId) => tags.find(({ id }) => selectedId === id))
|
|
39
|
+
.filter((notUndefined) => notUndefined) as unknown as TagType[];
|
|
40
|
+
return returnTags;
|
|
41
|
+
};
|
|
40
42
|
|
|
41
43
|
const getSuggestions = (tags: TagType[], inputValue: string): TagType[] => {
|
|
42
44
|
if (inputValue === '') {
|
package/src/Topic/Topic.tsx
CHANGED
|
@@ -21,7 +21,7 @@ import Loader from './Loader';
|
|
|
21
21
|
import { ItemProps } from '../Navigation/NavigationBox';
|
|
22
22
|
import { NavigationBox } from '../Navigation';
|
|
23
23
|
import { makeSrcQueryString, ImageCrop, ImageFocalPoint } from '../Image';
|
|
24
|
-
import { MessageBox
|
|
24
|
+
import { MessageBox } from '../Messages';
|
|
25
25
|
|
|
26
26
|
type InvertItProps = {
|
|
27
27
|
invertedStyle?: boolean;
|
|
@@ -331,7 +331,7 @@ const Topic = ({
|
|
|
331
331
|
</StyledAdditionalResource>
|
|
332
332
|
)}
|
|
333
333
|
</TopicHeading>
|
|
334
|
-
{messageBox && <MessageBox
|
|
334
|
+
{messageBox && <MessageBox>{messageBox}</MessageBox>}
|
|
335
335
|
<TopicIntroduction invertedStyle={invertedStyle}>
|
|
336
336
|
{renderMarkdown ? parse(renderMarkdown(topic.introduction)) : topic.introduction}
|
|
337
337
|
</TopicIntroduction>
|
|
@@ -28,7 +28,7 @@ import Logo from '../Logo';
|
|
|
28
28
|
import FrontpageAllSubjects from '../Frontpage/FrontpageAllSubjects';
|
|
29
29
|
import NavigationBox from '../Navigation/NavigationBox';
|
|
30
30
|
import { ProgrammeSubjects } from '../Programme';
|
|
31
|
-
import {
|
|
31
|
+
import { MessageBanner } from '../Messages';
|
|
32
32
|
|
|
33
33
|
const classes = new BEMHelper({
|
|
34
34
|
name: 'topic-menu',
|
|
@@ -153,7 +153,7 @@ export const TopicMenu = ({
|
|
|
153
153
|
return (
|
|
154
154
|
<nav>
|
|
155
155
|
{messages?.map((message) => (
|
|
156
|
-
<
|
|
156
|
+
<MessageBanner>{message}</MessageBanner>
|
|
157
157
|
))}
|
|
158
158
|
<ModalHeader modifier={['white', 'menu']}>
|
|
159
159
|
<div {...classes('masthead-left')}>
|
|
@@ -11,7 +11,7 @@ import styled from '@emotion/styled';
|
|
|
11
11
|
import { ArrowDropDown } from '@ndla/icons/common';
|
|
12
12
|
import { FolderOutlined } from '@ndla/icons/contentType';
|
|
13
13
|
import { colors, spacing, misc, animations } from '@ndla/core';
|
|
14
|
-
import { SetFocusedFolderId } from './TreeStructure.types';
|
|
14
|
+
import { SetFocusedFolderId, FolderChildFuncType } from './TreeStructure.types';
|
|
15
15
|
|
|
16
16
|
const OpenButton = styled.button<{ isOpen: boolean }>`
|
|
17
17
|
background: transparent;
|
|
@@ -37,6 +37,17 @@ const FolderItemWrapper = styled.div`
|
|
|
37
37
|
align-items: center;
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
|
+
const WrapperForFolderChild = styled.div<{ marked: boolean }>`
|
|
41
|
+
position: absolute;
|
|
42
|
+
right: ${spacing.xsmall};
|
|
43
|
+
opacity: ${({ marked }) => (marked ? 1 : 0.25)};
|
|
44
|
+
&:hover,
|
|
45
|
+
&:focus,
|
|
46
|
+
&:focus-within {
|
|
47
|
+
opacity: 1;
|
|
48
|
+
}
|
|
49
|
+
`;
|
|
50
|
+
|
|
40
51
|
const FolderName = styled.button<{ marked: boolean; noArrow?: boolean }>`
|
|
41
52
|
line-height: 1;
|
|
42
53
|
background: ${({ marked }) => (marked ? colors.brand.lighter : 'transparent')};
|
|
@@ -45,6 +56,9 @@ const FolderName = styled.button<{ marked: boolean; noArrow?: boolean }>`
|
|
|
45
56
|
&:focus {
|
|
46
57
|
background: ${({ marked }) => (marked ? colors.brand.light : colors.brand.lightest)};
|
|
47
58
|
color: ${colors.brand.primary};
|
|
59
|
+
+ ${WrapperForFolderChild} {
|
|
60
|
+
opacity: 1;
|
|
61
|
+
}
|
|
48
62
|
}
|
|
49
63
|
transition: ${animations.durations.superFast};
|
|
50
64
|
border: 0;
|
|
@@ -58,6 +72,7 @@ const FolderName = styled.button<{ marked: boolean; noArrow?: boolean }>`
|
|
|
58
72
|
margin-left: ${({ noArrow }) => (noArrow ? `29px` : `0px`)};
|
|
59
73
|
flex-grow: 1;
|
|
60
74
|
box-shadow: none;
|
|
75
|
+
text-align: left;
|
|
61
76
|
`;
|
|
62
77
|
|
|
63
78
|
const FolderNameLink = FolderName.withComponent('a');
|
|
@@ -77,6 +92,7 @@ interface Props {
|
|
|
77
92
|
url?: string;
|
|
78
93
|
icon?: React.ReactNode;
|
|
79
94
|
noPaddingWhenArrowIsHidden?: boolean;
|
|
95
|
+
folderChild?: FolderChildFuncType;
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
const FolderItem = ({
|
|
@@ -94,6 +110,7 @@ const FolderItem = ({
|
|
|
94
110
|
icon,
|
|
95
111
|
url,
|
|
96
112
|
noPaddingWhenArrowIsHidden,
|
|
113
|
+
folderChild,
|
|
97
114
|
}: Props) => {
|
|
98
115
|
const folderNameLinkRef = useRef<HTMLAnchorElement | null>(null);
|
|
99
116
|
const folderNameButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
@@ -134,24 +151,31 @@ const FolderItem = ({
|
|
|
134
151
|
{name}
|
|
135
152
|
</FolderNameLink>
|
|
136
153
|
) : (
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
<>
|
|
155
|
+
<FolderName
|
|
156
|
+
ref={folderNameButtonRef}
|
|
157
|
+
noArrow={hideArrow && !noPaddingWhenArrowIsHidden}
|
|
158
|
+
tabIndex={marked ? 0 : -1}
|
|
159
|
+
marked={marked}
|
|
160
|
+
disabled={loading}
|
|
161
|
+
onFocus={() => {
|
|
162
|
+
setFocusedFolderId(id);
|
|
163
|
+
}}
|
|
164
|
+
onClick={() => {
|
|
165
|
+
onMarkFolder(id);
|
|
166
|
+
if (openOnFolderClick) {
|
|
167
|
+
onToggleOpen(id);
|
|
168
|
+
}
|
|
169
|
+
}}>
|
|
170
|
+
{icon || <FolderOutlined />}
|
|
171
|
+
{name}
|
|
172
|
+
</FolderName>
|
|
173
|
+
{folderChild && (
|
|
174
|
+
<WrapperForFolderChild marked={marked}>
|
|
175
|
+
{folderChild(id, marked || id === focusedFolderId ? 0 : -1)}
|
|
176
|
+
</WrapperForFolderChild>
|
|
177
|
+
)}
|
|
178
|
+
</>
|
|
155
179
|
)}
|
|
156
180
|
</FolderItemWrapper>
|
|
157
181
|
);
|
|
@@ -48,6 +48,7 @@ const FolderItems = ({
|
|
|
48
48
|
focusedFolderId,
|
|
49
49
|
setFocusedFolderId,
|
|
50
50
|
firstLevel,
|
|
51
|
+
folderChild,
|
|
51
52
|
}: FolderItemsProps) => (
|
|
52
53
|
<StyledUL role="group" firstLevel={firstLevel}>
|
|
53
54
|
{data.map(({ name, data: dataChildren, id, url, icon }, _index) => {
|
|
@@ -71,6 +72,7 @@ const FolderItems = ({
|
|
|
71
72
|
hideArrow={dataChildren?.length === 0 || newIdPaths.length >= MAX_LEVEL_FOR_FOLDERS}
|
|
72
73
|
noPaddingWhenArrowIsHidden={editable && firstLevel && dataChildren?.length === 0}
|
|
73
74
|
setFocusedFolderId={setFocusedFolderId}
|
|
75
|
+
folderChild={folderChild}
|
|
74
76
|
/>
|
|
75
77
|
</div>
|
|
76
78
|
{newFolder?.parentId === id && (
|
|
@@ -98,6 +100,7 @@ const FolderItems = ({
|
|
|
98
100
|
focusedFolderId={focusedFolderId}
|
|
99
101
|
setFocusedFolderId={setFocusedFolderId}
|
|
100
102
|
firstLevel={false}
|
|
103
|
+
folderChild={folderChild}
|
|
101
104
|
/>
|
|
102
105
|
)}
|
|
103
106
|
</StyledLI>
|
|
@@ -40,6 +40,7 @@ const TreeStructure = ({
|
|
|
40
40
|
framed,
|
|
41
41
|
folderIdMarkedByDefault,
|
|
42
42
|
defaultOpenFolders,
|
|
43
|
+
folderChild,
|
|
43
44
|
}: TreeStructureProps) => {
|
|
44
45
|
const { t } = useTranslation();
|
|
45
46
|
const [newFolder, setNewFolder] = useState<NewFolderProps | undefined>();
|
|
@@ -152,6 +153,7 @@ const TreeStructure = ({
|
|
|
152
153
|
focusedFolderId={focusedFolderId}
|
|
153
154
|
setFocusedFolderId={setFocusedFolderId}
|
|
154
155
|
firstLevel
|
|
156
|
+
folderChild={folderChild}
|
|
155
157
|
/>
|
|
156
158
|
</TreeStructureStyledWrapper>
|
|
157
159
|
{editable && (
|