@ndla/ui 22.3.0 → 24.1.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/ArticleAuthorContent.js +2 -4
- package/es/Article/ArticleFavoritesButton.js +2 -2
- package/es/AuthorInfo/AuthorInfo.js +29 -16
- package/es/ContentCard/ContentCard.js +66 -25
- package/es/FileList/File.js +34 -8
- package/es/FileList/FileList.js +29 -3
- package/es/InfoBox/InfoBox.js +10 -3
- package/es/InfoWidget/InfoWidget.js +67 -22
- package/es/MyNdla/Resource/FolderInput.js +19 -6
- package/es/NoContentBox/NoContentBox.js +1 -6
- package/es/Portrait/Portrait.js +19 -13
- package/es/Resource/BlockResource.js +7 -6
- package/es/Resource/ListResource.js +8 -7
- package/es/ResourceBox/ResourceBox.js +7 -35
- package/es/Search/ActiveFilterContent.js +4 -14
- package/es/Search/ActiveFilters.js +8 -19
- package/es/Search/SearchField.js +31 -52
- package/es/Search/SearchResult.js +113 -136
- package/es/Search/ToggleSearchButton.js +34 -43
- package/es/Search/index.js +2 -8
- package/es/TreeStructure/FolderItems.js +3 -3
- package/es/TreeStructure/FolderNameInput.js +32 -14
- package/es/all.css +1 -1
- package/es/index-javascript.js +0 -1
- package/es/index.js +2 -1
- package/es/locale/messages-en.js +3 -3
- package/es/locale/messages-nb.js +3 -3
- package/es/locale/messages-nn.js +3 -3
- package/es/locale/messages-se.js +3 -3
- package/es/locale/messages-sma.js +3 -3
- package/lib/Article/ArticleAuthorContent.js +9 -4
- package/lib/Article/ArticleFavoritesButton.js +2 -2
- package/lib/AuthorInfo/AuthorInfo.d.ts +1 -11
- package/lib/AuthorInfo/AuthorInfo.js +36 -20
- package/lib/ContentCard/ContentCard.d.ts +1 -15
- package/lib/ContentCard/ContentCard.js +60 -28
- package/lib/FileList/File.js +36 -12
- package/lib/FileList/FileList.js +28 -5
- package/lib/InfoBox/InfoBox.js +12 -5
- package/lib/InfoWidget/InfoWidget.js +61 -25
- package/lib/MyNdla/Resource/FolderInput.js +18 -5
- package/lib/NoContentBox/NoContentBox.js +1 -8
- package/lib/Portrait/Portrait.js +19 -14
- package/lib/Resource/BlockResource.js +7 -6
- package/lib/Resource/ListResource.js +8 -7
- package/lib/ResourceBox/ResourceBox.d.ts +1 -4
- package/lib/ResourceBox/ResourceBox.js +15 -35
- package/lib/Search/ActiveFilterContent.d.ts +13 -0
- package/lib/Search/ActiveFilterContent.js +4 -15
- package/lib/Search/ActiveFilters.d.ts +13 -0
- package/lib/Search/ActiveFilters.js +8 -20
- package/lib/Search/SearchField.d.ts +19 -0
- package/lib/Search/SearchField.js +32 -56
- package/lib/Search/SearchResult.d.ts +36 -0
- package/lib/Search/SearchResult.js +116 -159
- package/lib/Search/ToggleSearchButton.d.ts +16 -0
- package/lib/Search/ToggleSearchButton.js +36 -46
- package/lib/Search/index.d.ts +12 -0
- package/lib/Search/index.js +0 -54
- package/lib/SearchTypeResult/SearchTypeHeader.d.ts +1 -1
- package/lib/TreeStructure/FolderItems.js +3 -3
- package/lib/TreeStructure/FolderNameInput.js +34 -14
- package/lib/TreeStructure/types.d.ts +1 -1
- package/lib/all.css +1 -1
- package/lib/index-javascript.js +0 -74
- package/lib/index.d.ts +1 -0
- package/lib/index.js +38 -1
- package/lib/locale/messages-en.js +3 -3
- package/lib/locale/messages-nb.js +3 -3
- package/lib/locale/messages-nn.js +3 -3
- package/lib/locale/messages-se.js +3 -3
- package/lib/locale/messages-sma.js +3 -3
- package/package.json +10 -10
- package/src/Article/ArticleAuthorContent.tsx +1 -1
- package/src/Article/ArticleFavoritesButton.tsx +2 -2
- package/src/AuthorInfo/AuthorInfo.tsx +53 -19
- package/src/ContentCard/ContentCard.tsx +127 -35
- package/src/FileList/File.tsx +47 -17
- package/src/FileList/FileList.tsx +37 -8
- package/src/InfoBox/InfoBox.tsx +24 -4
- package/src/InfoWidget/InfoWidget.tsx +83 -34
- package/src/MyNdla/Resource/FolderInput.tsx +18 -3
- package/src/NoContentBox/NoContentBox.tsx +2 -7
- package/src/Portrait/Portrait.tsx +25 -10
- package/src/Resource/BlockResource.tsx +1 -1
- package/src/Resource/ListResource.tsx +3 -1
- package/src/ResourceBox/ResourceBox.tsx +1 -20
- package/src/Search/{ActiveFilterContent.jsx → ActiveFilterContent.tsx} +11 -12
- package/src/Search/{ActiveFilters.jsx → ActiveFilters.tsx} +20 -17
- package/src/Search/{SearchField.jsx → SearchField.tsx} +58 -68
- package/src/Search/SearchResult.tsx +360 -0
- package/src/Search/ToggleSearchButton.tsx +73 -0
- package/src/Search/component.search.scss +0 -4
- package/src/Search/index.ts +16 -0
- package/src/SectionHeading/SectionHeading.tsx +1 -0
- package/src/TreeStructure/FolderItems.tsx +1 -1
- package/src/TreeStructure/FolderNameInput.tsx +33 -9
- package/src/TreeStructure/types.ts +1 -1
- package/src/all.scss +0 -1
- package/src/index-javascript.js +0 -15
- package/src/index.ts +2 -0
- package/src/locale/messages-en.ts +3 -3
- package/src/locale/messages-nb.ts +3 -3
- package/src/locale/messages-nn.ts +3 -3
- package/src/locale/messages-se.ts +3 -3
- package/src/locale/messages-sma.ts +3 -3
- package/src/main.scss +0 -8
- package/es/BackgroundImage/BackgroundImage.js +0 -27
- package/es/BackgroundImage/index.js +0 -2
- package/es/Search/SearchFilter.js +0 -72
- package/es/Search/SearchFilterList.js +0 -115
- package/es/Search/SearchOverlay.js +0 -39
- package/es/Search/SearchPage.js +0 -178
- package/es/Search/SearchPopoverFilter.js +0 -152
- package/es/Search/SearchResultAuthor.js +0 -51
- package/lib/BackgroundImage/BackgroundImage.d.ts +0 -12
- package/lib/BackgroundImage/BackgroundImage.js +0 -40
- package/lib/BackgroundImage/index.d.ts +0 -2
- package/lib/BackgroundImage/index.js +0 -13
- package/lib/Search/SearchFilter.js +0 -88
- package/lib/Search/SearchFilterList.js +0 -137
- package/lib/Search/SearchOverlay.js +0 -62
- package/lib/Search/SearchPage.js +0 -207
- package/lib/Search/SearchPopoverFilter.js +0 -172
- package/lib/Search/SearchResultAuthor.js +0 -60
- package/src/AuthorInfo/component.author-info.scss +0 -54
- package/src/BackgroundImage/BackgroundImage.tsx +0 -32
- package/src/BackgroundImage/component.background-image.scss +0 -52
- package/src/BackgroundImage/index.ts +0 -3
- package/src/ContentCard/component.content-card.scss +0 -109
- package/src/FileList/component.file-list.scss +0 -102
- package/src/InfoBox/component.info-box.scss +0 -21
- package/src/InfoWidget/component.info-widget.scss +0 -52
- package/src/NoContentBox/component.no-content-box.scss +0 -17
- package/src/Portrait/component.portrait.scss +0 -29
- package/src/Search/SearchFilter.jsx +0 -82
- package/src/Search/SearchFilterList.jsx +0 -110
- package/src/Search/SearchOverlay.jsx +0 -38
- package/src/Search/SearchPage.jsx +0 -178
- package/src/Search/SearchPopoverFilter.jsx +0 -109
- package/src/Search/SearchResult.jsx +0 -239
- package/src/Search/SearchResultAuthor.jsx +0 -54
- package/src/Search/ToggleSearchButton.jsx +0 -64
- package/src/Search/component.search-filter.scss +0 -67
- package/src/Search/component.search-overlay.scss +0 -103
- package/src/Search/component.search-page.scss +0 -125
- package/src/Search/component.search-result-author.scss +0 -65
- package/src/Search/index.js +0 -34
package/src/FileList/File.tsx
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
1
3
|
import { Download } from '@ndla/icons/common';
|
|
2
4
|
import SafeLink from '@ndla/safelink';
|
|
3
5
|
import Tooltip from '@ndla/tooltip';
|
|
4
6
|
import React from 'react';
|
|
5
|
-
import BEMHelper from 'react-bem-helper';
|
|
6
7
|
import { FileFormat, FileType } from './FileList';
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
+
const LinkText = styled.span`
|
|
10
|
+
& > span {
|
|
11
|
+
box-shadow: inset 0 -1px;
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
const FileLink = styled(SafeLink)`
|
|
15
|
+
box-shadow: none;
|
|
16
|
+
position: relative;
|
|
17
|
+
color: ${colors.brand.primary};
|
|
18
|
+
margin-right: ${spacing.normal};
|
|
19
|
+
display: flex;
|
|
20
|
+
align-items: center;
|
|
21
|
+
|
|
22
|
+
&:last-child {
|
|
23
|
+
margin-right: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
&:hover,
|
|
27
|
+
&:focus,
|
|
28
|
+
&:active {
|
|
29
|
+
${LinkText} {
|
|
30
|
+
box-shadow: none;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
`;
|
|
9
34
|
|
|
10
35
|
const renderFormat = (format: FileFormat, title: string, isPrimary: boolean, id: string, isDeadLink: boolean) => {
|
|
11
36
|
const titleWithFormat = `${title} (${format.fileType.toUpperCase()})`;
|
|
@@ -22,20 +47,14 @@ const renderFormat = (format: FileFormat, title: string, isPrimary: boolean, id:
|
|
|
22
47
|
}
|
|
23
48
|
|
|
24
49
|
return (
|
|
25
|
-
<
|
|
26
|
-
{...classes('link')}
|
|
27
|
-
key={format.url}
|
|
28
|
-
to={format.url}
|
|
29
|
-
target="_blank"
|
|
30
|
-
aria-label={titleWithFormat}
|
|
31
|
-
aria-describedby={formatId}>
|
|
50
|
+
<FileLink key={format.url} to={format.url} target="_blank" aria-label={titleWithFormat} aria-describedby={formatId}>
|
|
32
51
|
<Download />
|
|
33
52
|
<Tooltip tooltip={format.tooltip}>
|
|
34
|
-
<
|
|
53
|
+
<LinkText>
|
|
35
54
|
<span>{isPrimary ? titleWithFormat : `(${format.fileType.toUpperCase()})`}</span>
|
|
36
|
-
</
|
|
55
|
+
</LinkText>
|
|
37
56
|
</Tooltip>
|
|
38
|
-
</
|
|
57
|
+
</FileLink>
|
|
39
58
|
);
|
|
40
59
|
};
|
|
41
60
|
|
|
@@ -44,16 +63,27 @@ interface Props {
|
|
|
44
63
|
file: FileType;
|
|
45
64
|
}
|
|
46
65
|
|
|
66
|
+
const FileListItem = styled.li`
|
|
67
|
+
${fonts.sizes('18px', '26px')};
|
|
68
|
+
font-weight: ${fonts.weight.semibold};
|
|
69
|
+
min-height: 60px;
|
|
70
|
+
background: ${colors.brand.greyLighter};
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
flex-wrap: wrap;
|
|
74
|
+
margin-bottom: ${spacing.xsmall};
|
|
75
|
+
padding: ${spacing.small};
|
|
76
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
77
|
+
padding: ${spacing.small} ${spacing.normal};
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
|
|
47
81
|
const File = ({ file, id }: Props) => {
|
|
48
82
|
const formatLinks = file.formats.map((format, index) =>
|
|
49
83
|
renderFormat(format, file.title, index === 0, id, !file.fileExists),
|
|
50
84
|
);
|
|
51
85
|
|
|
52
|
-
return
|
|
53
|
-
<li {...classes('item')} key={file.title}>
|
|
54
|
-
{formatLinks}
|
|
55
|
-
</li>
|
|
56
|
-
);
|
|
86
|
+
return <FileListItem key={file.title}>{formatLinks}</FileListItem>;
|
|
57
87
|
};
|
|
58
88
|
|
|
59
89
|
export default File;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import
|
|
2
|
+
import styled from '@emotion/styled';
|
|
3
|
+
import { colors, fonts, spacing } from '@ndla/core';
|
|
3
4
|
import File from './File';
|
|
4
5
|
|
|
5
|
-
const classes = BEMHelper('c-file-list');
|
|
6
|
-
|
|
7
6
|
export interface FileType {
|
|
8
7
|
title: string;
|
|
9
8
|
formats: FileFormat[];
|
|
@@ -22,15 +21,45 @@ interface Props {
|
|
|
22
21
|
files: FileType[];
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
const FileListSection = styled.section`
|
|
25
|
+
margin: ${spacing.large} 0;
|
|
26
|
+
padding: ${spacing.small} 0 ${spacing.normal} ${spacing.normal};
|
|
27
|
+
border-left: 2px solid ${colors.brand.greyLightest};
|
|
28
|
+
font-family: ${fonts.sans};
|
|
29
|
+
|
|
30
|
+
.c-icon {
|
|
31
|
+
margin-top: 3px;
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
margin-right: ${spacing.small};
|
|
34
|
+
height: 18px;
|
|
35
|
+
width: 18px;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const FileListHeading = styled.h1`
|
|
40
|
+
${fonts.sizes('16px', '18px')};
|
|
41
|
+
letter-spacing: 0.05em;
|
|
42
|
+
margin: 0 0 ${spacing.xsmall} 0;
|
|
43
|
+
padding-bottom: ${spacing.xsmall};
|
|
44
|
+
border-bottom: 2px solid ${colors.brand.greyLight};
|
|
45
|
+
font-weight: ${fonts.weight.bold};
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
`;
|
|
48
|
+
|
|
49
|
+
const FilesList = styled.ul`
|
|
50
|
+
margin: 0;
|
|
51
|
+
padding: 0;
|
|
52
|
+
`;
|
|
53
|
+
|
|
25
54
|
const FileList = ({ files, heading, id }: Props) => (
|
|
26
|
-
<
|
|
27
|
-
<
|
|
28
|
-
<
|
|
55
|
+
<FileListSection>
|
|
56
|
+
<FileListHeading>{heading}</FileListHeading>
|
|
57
|
+
<FilesList>
|
|
29
58
|
{files.map((file) => (
|
|
30
59
|
<File key={`file-${id}-${file.title}`} file={file} id={id} />
|
|
31
60
|
))}
|
|
32
|
-
</
|
|
33
|
-
</
|
|
61
|
+
</FilesList>
|
|
62
|
+
</FileListSection>
|
|
34
63
|
);
|
|
35
64
|
|
|
36
65
|
export default FileList;
|
package/src/InfoBox/InfoBox.tsx
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
|
|
1
3
|
import React, { ReactNode } from 'react';
|
|
2
|
-
import BEMHelper from 'react-bem-helper';
|
|
3
|
-
|
|
4
|
-
const classes = BEMHelper('c-info-box');
|
|
5
4
|
|
|
6
5
|
interface Props {
|
|
7
6
|
children?: ReactNode;
|
|
8
7
|
}
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
const StyledInfoBox = styled.div`
|
|
10
|
+
background: ${colors.support.yellowLight};
|
|
11
|
+
padding: ${spacing.small} ${spacing.normal};
|
|
12
|
+
font-family: ${fonts.sans};
|
|
13
|
+
${fonts.sizes('14px', '18px')};
|
|
14
|
+
margin-bottom: ${spacing.medium};
|
|
15
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
16
|
+
${fonts.sizes('16px', '20px')};
|
|
17
|
+
padding: ${spacing.normal};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
& > *:first-child {
|
|
21
|
+
margin-top: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
& > *:last-child {
|
|
25
|
+
margin-bottom: 0;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const InfoBox = ({ children }: Props) => <StyledInfoBox>{children}</StyledInfoBox>;
|
|
10
30
|
|
|
11
31
|
export default InfoBox;
|
|
@@ -1,13 +1,67 @@
|
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
import { css } from '@emotion/core';
|
|
1
3
|
import React, { ReactNode } from 'react';
|
|
2
|
-
import BEMHelper from 'react-bem-helper';
|
|
3
4
|
import { Forward } from '@ndla/icons/common';
|
|
4
|
-
|
|
5
5
|
import SafeLink from '@ndla/safelink';
|
|
6
|
+
import { colors, fonts, spacing } from '@ndla/core';
|
|
6
7
|
|
|
7
8
|
import SectionHeading from '../SectionHeading';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
interface InfoWidgetSectionProps {
|
|
11
|
+
center?: boolean;
|
|
12
|
+
}
|
|
13
|
+
const InfoWidgetSection = styled.section<InfoWidgetSectionProps>`
|
|
14
|
+
max-width: 600px;
|
|
15
|
+
margin: ${(p) => p.center && '0 auto'};
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const InfoWidgetDescription = styled.div`
|
|
19
|
+
padding: ${spacing.normal};
|
|
20
|
+
background: ${colors.brand.lighter};
|
|
21
|
+
color: ${colors.brand.dark};
|
|
22
|
+
${fonts.sizes('18px', '26px')};
|
|
23
|
+
|
|
24
|
+
p {
|
|
25
|
+
margin-top: 0;
|
|
26
|
+
&:last-child {
|
|
27
|
+
margin-bottom: 0;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const InfoWidgetLinksContainer = styled.div`
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: flex-end;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const iconLinkStyle = css`
|
|
39
|
+
width: 47px;
|
|
40
|
+
height: 47px;
|
|
41
|
+
border-radius: 100%;
|
|
42
|
+
background: ${colors.brand.lighter};
|
|
43
|
+
color: ${colors.brand.dark};
|
|
44
|
+
box-shadow: none;
|
|
45
|
+
display: flex;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
align-items: center;
|
|
48
|
+
margin-right: ${spacing.small};
|
|
10
49
|
|
|
50
|
+
.c-icon {
|
|
51
|
+
width: 20px;
|
|
52
|
+
height: 20px;
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const IconSafeLink = styled(SafeLink)(iconLinkStyle);
|
|
57
|
+
|
|
58
|
+
const IconAnchor = styled.a(iconLinkStyle);
|
|
59
|
+
const IconSpan = styled.span(iconLinkStyle);
|
|
60
|
+
|
|
61
|
+
const StyledMainLink = styled.a`
|
|
62
|
+
color: ${colors.brand.dark};
|
|
63
|
+
${fonts.sizes('16px', '24px')};
|
|
64
|
+
`;
|
|
11
65
|
interface Props {
|
|
12
66
|
heading: string;
|
|
13
67
|
description: string;
|
|
@@ -26,36 +80,33 @@ interface Props {
|
|
|
26
80
|
}
|
|
27
81
|
|
|
28
82
|
const InfoWidget = ({ heading, description, mainLink, iconLinks, center = false }: Props) => (
|
|
29
|
-
<
|
|
30
|
-
<SectionHeading large
|
|
31
|
-
|
|
32
|
-
</SectionHeading>
|
|
33
|
-
<div {...classes('description')}>
|
|
83
|
+
<InfoWidgetSection center={center}>
|
|
84
|
+
<SectionHeading large>{heading}</SectionHeading>
|
|
85
|
+
<InfoWidgetDescription>
|
|
34
86
|
<p>{description}</p>
|
|
35
|
-
</
|
|
36
|
-
<
|
|
37
|
-
{iconLinks
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (link.href) {
|
|
47
|
-
return (
|
|
48
|
-
<a key={link.href} href={link.href} {...classes('icon-link')} aria-label={link.name}>
|
|
49
|
-
{link.icon}
|
|
50
|
-
</a>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
87
|
+
</InfoWidgetDescription>
|
|
88
|
+
<InfoWidgetLinksContainer>
|
|
89
|
+
{iconLinks?.map((link) => {
|
|
90
|
+
if (link.url) {
|
|
91
|
+
return (
|
|
92
|
+
<IconSafeLink key={link.url} to={link.url} aria-label={link.name}>
|
|
93
|
+
{link.icon}
|
|
94
|
+
</IconSafeLink>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (link.href) {
|
|
53
98
|
return (
|
|
54
|
-
<
|
|
99
|
+
<IconAnchor key={link.href} href={link.href} aria-label={link.name}>
|
|
55
100
|
{link.icon}
|
|
56
|
-
</
|
|
101
|
+
</IconAnchor>
|
|
57
102
|
);
|
|
58
|
-
}
|
|
103
|
+
}
|
|
104
|
+
return (
|
|
105
|
+
<IconSpan key={link.name} aria-label={link.name}>
|
|
106
|
+
{link.icon}
|
|
107
|
+
</IconSpan>
|
|
108
|
+
);
|
|
109
|
+
})}
|
|
59
110
|
{mainLink.url ? (
|
|
60
111
|
<div className="o-text-link__wrapper o-text-link__wrapper--right">
|
|
61
112
|
<SafeLink className="o-text-link" to={mainLink.url}>
|
|
@@ -63,12 +114,10 @@ const InfoWidget = ({ heading, description, mainLink, iconLinks, center = false
|
|
|
63
114
|
</SafeLink>
|
|
64
115
|
</div>
|
|
65
116
|
) : (
|
|
66
|
-
<
|
|
67
|
-
{mainLink.name}
|
|
68
|
-
</a>
|
|
117
|
+
<StyledMainLink href={mainLink.href}>{mainLink.name}</StyledMainLink>
|
|
69
118
|
)}
|
|
70
|
-
</
|
|
71
|
-
</
|
|
119
|
+
</InfoWidgetLinksContainer>
|
|
120
|
+
</InfoWidgetSection>
|
|
72
121
|
);
|
|
73
122
|
|
|
74
123
|
export default InfoWidget;
|
|
@@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
15
15
|
import { colors, spacing } from '@ndla/core';
|
|
16
16
|
import { Input } from '@ndla/forms';
|
|
17
17
|
import { css } from '@emotion/core';
|
|
18
|
+
import { Done } from '@ndla/icons/editor';
|
|
18
19
|
|
|
19
20
|
// Source: https://kovart.github.io/dashed-border-generator/
|
|
20
21
|
const borderStyle = `url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' stroke='${encodeURIComponent(
|
|
@@ -40,6 +41,9 @@ const inputWrapperStyle = css`
|
|
|
40
41
|
|
|
41
42
|
const StyledInput = styled(Input)`
|
|
42
43
|
&& {
|
|
44
|
+
flex-grow: 1;
|
|
45
|
+
flex-basis: 0;
|
|
46
|
+
min-width: 0;
|
|
43
47
|
line-height: 1.75em;
|
|
44
48
|
color: ${colors.brand.primary};
|
|
45
49
|
::selection {
|
|
@@ -48,6 +52,12 @@ const StyledInput = styled(Input)`
|
|
|
48
52
|
}
|
|
49
53
|
`;
|
|
50
54
|
|
|
55
|
+
const Row = styled.div`
|
|
56
|
+
display: flex;
|
|
57
|
+
gap: ${spacing.xsmall};
|
|
58
|
+
padding-right: ${spacing.small};
|
|
59
|
+
`;
|
|
60
|
+
|
|
51
61
|
interface Props {
|
|
52
62
|
onAddFolder: (name: string) => void;
|
|
53
63
|
onClose: () => void;
|
|
@@ -90,9 +100,14 @@ const FolderInput = ({ onAddFolder, onClose, autoSelect }: Props) => {
|
|
|
90
100
|
</StyledFolderIcon>
|
|
91
101
|
}
|
|
92
102
|
iconRight={
|
|
93
|
-
<
|
|
94
|
-
<
|
|
95
|
-
|
|
103
|
+
<Row>
|
|
104
|
+
<IconButton aria-label={t('close')} size="small" ghostPill onClick={onClose}>
|
|
105
|
+
<Cross />
|
|
106
|
+
</IconButton>
|
|
107
|
+
<IconButton aria-label={t('save')} size="small" ghostPill onClick={() => onAddFolder(input)}>
|
|
108
|
+
<Done />
|
|
109
|
+
</IconButton>
|
|
110
|
+
</Row>
|
|
96
111
|
}
|
|
97
112
|
/>
|
|
98
113
|
);
|
|
@@ -7,21 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
|
-
import BEMHelper from 'react-bem-helper';
|
|
11
10
|
import Button from '@ndla/button';
|
|
12
11
|
|
|
13
|
-
const classes = new BEMHelper({
|
|
14
|
-
name: 'topic-resource',
|
|
15
|
-
prefix: 'c-',
|
|
16
|
-
});
|
|
17
|
-
|
|
18
12
|
interface Props {
|
|
19
13
|
onClick?: () => void;
|
|
20
14
|
buttonText?: string;
|
|
21
15
|
text: string;
|
|
22
16
|
}
|
|
17
|
+
|
|
23
18
|
export const NoContentBox = ({ buttonText, text, onClick }: Props) => (
|
|
24
|
-
<div
|
|
19
|
+
<div>
|
|
25
20
|
<span>
|
|
26
21
|
<div>
|
|
27
22
|
<p>{text}</p>
|
|
@@ -6,13 +6,25 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import styled from '@emotion/styled';
|
|
9
10
|
import React from 'react';
|
|
10
|
-
import BEMHelper from 'react-bem-helper';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
interface PortraitWrapperProps {
|
|
13
|
+
size: string;
|
|
14
|
+
}
|
|
15
|
+
const PortraitWrapper = styled.div<PortraitWrapperProps>`
|
|
16
|
+
width: ${(p) => p.size};
|
|
17
|
+
height: ${(p) => p.size};
|
|
18
|
+
span {
|
|
19
|
+
display: block;
|
|
20
|
+
border-radius: 50%;
|
|
21
|
+
background-color: rgba(0, 0, 0, 0.16);
|
|
22
|
+
background-size: cover;
|
|
23
|
+
background-position: center center;
|
|
24
|
+
width: ${(p) => p.size};
|
|
25
|
+
height: ${(p) => p.size};
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
16
28
|
|
|
17
29
|
interface Props {
|
|
18
30
|
src: string;
|
|
@@ -21,10 +33,13 @@ interface Props {
|
|
|
21
33
|
modifier?: 'small' | 'large';
|
|
22
34
|
}
|
|
23
35
|
|
|
24
|
-
const Portrait = ({ src, alt, modifier, className }: Props) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
36
|
+
const Portrait = ({ src, alt, modifier, className }: Props) => {
|
|
37
|
+
const size = modifier === 'small' ? '4rem' : modifier === 'large' ? '10rem' : '7rem';
|
|
38
|
+
return (
|
|
39
|
+
<PortraitWrapper size={size} className={className}>
|
|
40
|
+
<span role="img" aria-label={alt} style={{ backgroundImage: `url(${src})` }} />
|
|
41
|
+
</PortraitWrapper>
|
|
42
|
+
);
|
|
43
|
+
};
|
|
29
44
|
|
|
30
45
|
export default Portrait;
|
|
@@ -94,7 +94,7 @@ const BlockImage = ({ image, loading }: BlockImageProps) => {
|
|
|
94
94
|
</ContentLoader>
|
|
95
95
|
);
|
|
96
96
|
}
|
|
97
|
-
return <Image alt={image.alt} src={image.src} />;
|
|
97
|
+
return <Image alt={image.alt} src={image.src} fallbackWidth={300} />;
|
|
98
98
|
};
|
|
99
99
|
|
|
100
100
|
interface BlockTitleProps {
|
|
@@ -127,7 +127,9 @@ interface ListResourceImageProps {
|
|
|
127
127
|
|
|
128
128
|
const ListResourceImage = ({ resourceImage, loading, type }: ListResourceImageProps) => {
|
|
129
129
|
if (!loading) {
|
|
130
|
-
return
|
|
130
|
+
return (
|
|
131
|
+
<StyledImage alt={resourceImage.alt} src={resourceImage.src} fallbackWidth={type === 'compact' ? 56 : 136} />
|
|
132
|
+
);
|
|
131
133
|
}
|
|
132
134
|
return (
|
|
133
135
|
<ContentLoader height={'100%'} width={'100%'} viewBox={null} preserveAspectRatio="none">
|
|
@@ -10,7 +10,6 @@ import React from 'react';
|
|
|
10
10
|
import { breakpoints, fonts, mq, colors, spacing } from '@ndla/core';
|
|
11
11
|
import { useTranslation } from 'react-i18next';
|
|
12
12
|
import { Launch } from '@ndla/icons/common';
|
|
13
|
-
import { LicenseByline } from '@ndla/licenses';
|
|
14
13
|
import { SafeLinkButton } from '@ndla/safelink';
|
|
15
14
|
import styled from '@emotion/styled';
|
|
16
15
|
import Image from '../Image';
|
|
@@ -81,12 +80,6 @@ const StyledImage = styled(Image)`
|
|
|
81
80
|
}
|
|
82
81
|
`;
|
|
83
82
|
|
|
84
|
-
const LincenseWrapper = styled.div`
|
|
85
|
-
position: absolute;
|
|
86
|
-
top: 9px;
|
|
87
|
-
right: 0;
|
|
88
|
-
`;
|
|
89
|
-
|
|
90
83
|
interface ImageMeta {
|
|
91
84
|
src: string;
|
|
92
85
|
alt: string;
|
|
@@ -96,25 +89,13 @@ interface Props {
|
|
|
96
89
|
image: ImageMeta;
|
|
97
90
|
title: string;
|
|
98
91
|
caption: string;
|
|
99
|
-
licenseRights: string[];
|
|
100
|
-
authors?: string[];
|
|
101
|
-
locale?: string;
|
|
102
92
|
url: string;
|
|
103
93
|
}
|
|
104
94
|
|
|
105
|
-
export const ResourceBox = ({ image, title, caption,
|
|
95
|
+
export const ResourceBox = ({ image, title, caption, url }: Props) => {
|
|
106
96
|
const { t } = useTranslation();
|
|
107
97
|
return (
|
|
108
98
|
<ResourceBoxContainer>
|
|
109
|
-
<LincenseWrapper>
|
|
110
|
-
<LicenseByline licenseRights={licenseRights} locale={locale} marginRight color={colors.brand.tertiary}>
|
|
111
|
-
{authors && authors.length > 0 && (
|
|
112
|
-
<div className="c-figure__byline-author-buttons">
|
|
113
|
-
<span className="c-figure__byline-authors">{authors.join(' ')}</span>
|
|
114
|
-
</div>
|
|
115
|
-
)}
|
|
116
|
-
</LicenseByline>
|
|
117
|
-
</LincenseWrapper>
|
|
118
99
|
<StyledImage src={image.src} alt={image.alt} />
|
|
119
100
|
<ContentWrapper>
|
|
120
101
|
<Title>{title}</Title>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import styled from '@emotion/styled';
|
|
4
3
|
import { Cross } from '@ndla/icons/action';
|
|
5
4
|
import { spacing, mq, breakpoints, colors, misc, fonts } from '@ndla/core';
|
|
@@ -32,7 +31,17 @@ export const StyledActiveFilterTitle = styled('span')`
|
|
|
32
31
|
padding-right: ${spacing.small};
|
|
33
32
|
`;
|
|
34
33
|
|
|
35
|
-
|
|
34
|
+
interface Props {
|
|
35
|
+
filter: {
|
|
36
|
+
value: string;
|
|
37
|
+
title: string;
|
|
38
|
+
filterName?: string;
|
|
39
|
+
};
|
|
40
|
+
onFilterRemove: (value: string, filterName?: string) => void;
|
|
41
|
+
ariaLabel: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }: Props) => (
|
|
36
45
|
<StyledActiveFilter
|
|
37
46
|
aria-label={ariaLabel}
|
|
38
47
|
type="button"
|
|
@@ -42,14 +51,4 @@ const ActiveFilterContent = ({ filter, onFilterRemove, ariaLabel }) => (
|
|
|
42
51
|
</StyledActiveFilter>
|
|
43
52
|
);
|
|
44
53
|
|
|
45
|
-
ActiveFilterContent.propTypes = {
|
|
46
|
-
filter: PropTypes.shape({
|
|
47
|
-
value: PropTypes.string.isRequired,
|
|
48
|
-
title: PropTypes.string.isRequired,
|
|
49
|
-
filterName: PropTypes.string,
|
|
50
|
-
}),
|
|
51
|
-
onFilterRemove: PropTypes.func.isRequired,
|
|
52
|
-
ariaLabel: PropTypes.string.isRequired,
|
|
53
|
-
};
|
|
54
|
-
|
|
55
54
|
export default ActiveFilterContent;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import styled from '@emotion/styled';
|
|
4
3
|
import { css } from '@emotion/core';
|
|
5
4
|
import { spacing, mq, breakpoints } from '@ndla/core';
|
|
@@ -7,7 +6,12 @@ import Tooltip from '@ndla/tooltip';
|
|
|
7
6
|
import { useTranslation } from 'react-i18next';
|
|
8
7
|
import ActiveFilterContent, { StyledActiveFilterTitle } from './ActiveFilterContent';
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
interface StyledActiveFiltersProps {
|
|
10
|
+
showOnSmallScreen?: boolean;
|
|
11
|
+
filterLength?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const StyledActiveFilters = styled('ul')<StyledActiveFiltersProps>`
|
|
11
15
|
margin: 0;
|
|
12
16
|
padding: 0;
|
|
13
17
|
flex-direction: column;
|
|
@@ -70,10 +74,22 @@ const StyledActiveFilterWrapper = styled('li')`
|
|
|
70
74
|
}
|
|
71
75
|
`;
|
|
72
76
|
|
|
73
|
-
const getFilterLength = (filters) =>
|
|
77
|
+
const getFilterLength = (filters: Filter[]) =>
|
|
74
78
|
filters.filter((filter) => filter.filterName === 'filter_subjects' && filter.title).length;
|
|
75
79
|
|
|
76
|
-
|
|
80
|
+
interface Filter {
|
|
81
|
+
title: string;
|
|
82
|
+
value: string;
|
|
83
|
+
filterName?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface Props {
|
|
87
|
+
filters: Filter[];
|
|
88
|
+
onFilterRemove: (value: string, filterName?: string) => void;
|
|
89
|
+
showOnSmallScreen?: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }: Props) => {
|
|
77
93
|
const { t } = useTranslation();
|
|
78
94
|
if (filters && filters.length > 0) {
|
|
79
95
|
const filterLength = getFilterLength(filters);
|
|
@@ -90,7 +106,6 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
|
|
|
90
106
|
})}>
|
|
91
107
|
<ActiveFilterContent
|
|
92
108
|
filter={filter}
|
|
93
|
-
filterLength={filterLength}
|
|
94
109
|
ariaLabel={t('searchPage.searchFilterMessages.removeFilter', {
|
|
95
110
|
filterName: filter.title,
|
|
96
111
|
})}
|
|
@@ -100,7 +115,6 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
|
|
|
100
115
|
) : (
|
|
101
116
|
<ActiveFilterContent
|
|
102
117
|
filter={filter}
|
|
103
|
-
filterLength={filterLength}
|
|
104
118
|
onFilterRemove={onFilterRemove}
|
|
105
119
|
ariaLabel={t('searchPage.searchFilterMessages.removeFilter', {
|
|
106
120
|
filterName: filter.title,
|
|
@@ -121,15 +135,4 @@ const ActiveFilters = ({ filters, onFilterRemove, showOnSmallScreen }) => {
|
|
|
121
135
|
return null;
|
|
122
136
|
};
|
|
123
137
|
|
|
124
|
-
ActiveFilters.propTypes = {
|
|
125
|
-
filters: PropTypes.arrayOf(
|
|
126
|
-
PropTypes.shape({
|
|
127
|
-
value: PropTypes.string.isRequired,
|
|
128
|
-
title: PropTypes.string.isRequired,
|
|
129
|
-
filterName: PropTypes.string,
|
|
130
|
-
}),
|
|
131
|
-
),
|
|
132
|
-
onFilterRemove: PropTypes.func.isRequired,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
138
|
export default ActiveFilters;
|