@ndla/ui 24.2.2 → 25.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/CompetenceGoals/CompetenceGoalsDialog.js +15 -23
- package/es/CompetenceGoals/index.js +1 -4
- package/es/Filter/FilterButtons.js +10 -11
- package/es/Filter/FilterList.js +82 -152
- package/es/Filter/FilterListPhone.js +180 -271
- package/es/Filter/ToggleItem.js +6 -25
- package/es/Frontpage/FrontpageAllSubjects.js +8 -9
- package/es/Frontpage/FrontpageSearch.js +3 -4
- package/es/Masthead/Masthead.js +65 -41
- package/es/Masthead/MastheadSearchModal.js +4 -5
- package/es/Masthead/SkipToMainContent.js +24 -0
- package/es/Masthead/index.js +2 -1
- package/es/SearchTypeResult/PopupFilter.js +8 -9
- package/es/SearchTypeResult/SearchFilterContent.js +3 -4
- package/es/SectionHeading/SectionHeading.js +24 -17
- package/es/Subject/index.js +0 -1
- package/es/all.css +1 -1
- package/es/index-javascript.js +1 -4
- package/es/index.js +5 -2
- package/lib/CompetenceGoals/CompetenceGoalsDialog.d.ts +19 -0
- package/lib/CompetenceGoals/CompetenceGoalsDialog.js +13 -24
- package/lib/CompetenceGoals/index.d.ts +1 -0
- package/lib/CompetenceGoals/index.js +1 -33
- package/lib/Filter/FilterButtons.js +9 -9
- package/lib/Filter/FilterList.d.ts +25 -0
- package/lib/Filter/FilterList.js +85 -155
- package/lib/Filter/FilterListPhone.d.ts +32 -0
- package/lib/Filter/FilterListPhone.js +176 -269
- package/lib/Filter/ToggleItem.d.ts +15 -0
- package/lib/Filter/ToggleItem.js +6 -32
- package/lib/Filter/filterClasses.d.ts +2 -0
- package/lib/Filter/index.d.ts +12 -0
- package/lib/Frontpage/FrontpageAllSubjects.js +7 -7
- package/lib/Frontpage/FrontpageSearch.js +2 -3
- package/lib/Masthead/Masthead.d.ts +4 -8
- package/lib/Masthead/Masthead.js +65 -49
- package/lib/Masthead/MastheadSearchModal.js +3 -4
- package/lib/Masthead/SkipToMainContent.d.ts +6 -0
- package/lib/Masthead/SkipToMainContent.js +38 -0
- package/lib/Masthead/index.d.ts +2 -1
- package/lib/Masthead/index.js +10 -0
- package/lib/SearchTypeResult/PopupFilter.js +7 -7
- package/lib/SearchTypeResult/SearchFilterContent.js +2 -2
- package/lib/SectionHeading/SectionHeading.d.ts +1 -13
- package/lib/SectionHeading/SectionHeading.js +23 -19
- package/lib/Subject/index.d.ts +0 -1
- package/lib/Subject/index.js +0 -8
- package/lib/all.css +1 -1
- package/lib/index-javascript.js +1 -63
- package/lib/index.d.ts +5 -2
- package/lib/index.js +48 -7
- package/package.json +11 -11
- package/src/CompetenceGoals/{CompetenceGoalsDialog.jsx → CompetenceGoalsDialog.tsx} +34 -26
- package/src/CompetenceGoals/index.ts +1 -0
- package/src/Filter/FilterButtons.tsx +0 -1
- package/src/Filter/FilterList.tsx +135 -0
- package/src/Filter/FilterListPhone.tsx +275 -0
- package/src/Filter/ToggleItem.tsx +58 -0
- package/src/Filter/{filterClasses.js → filterClasses.ts} +0 -0
- package/src/Filter/{index.js → index.ts} +0 -0
- package/src/Frontpage/FrontpageAllSubjects.tsx +0 -1
- package/src/Frontpage/FrontpageSearch.tsx +0 -1
- package/src/Masthead/Masthead.tsx +85 -45
- package/src/Masthead/MastheadSearchModal.tsx +0 -1
- package/src/Masthead/SkipToMainContent.tsx +48 -0
- package/src/Masthead/index.ts +2 -1
- package/src/SearchTypeResult/PopupFilter.tsx +0 -1
- package/src/SearchTypeResult/SearchFilterContent.tsx +0 -1
- package/src/SectionHeading/SectionHeading.tsx +29 -16
- package/src/Subject/index.ts +0 -1
- package/src/index-javascript.js +0 -10
- package/src/index.ts +7 -2
- package/src/main.scss +0 -3
- package/es/CompetenceGoals/CompetenceGoalList.js +0 -58
- package/es/CompetenceGoals/CompetenceGoals.js +0 -159
- package/es/Subject/SubjectFilter.js +0 -42
- package/lib/CompetenceGoals/CompetenceGoalList.js +0 -78
- package/lib/CompetenceGoals/CompetenceGoals.js +0 -184
- package/lib/Subject/SubjectFilter.d.ts +0 -27
- package/lib/Subject/SubjectFilter.js +0 -58
- package/src/.DS_Store +0 -0
- package/src/CompetenceGoals/CompetenceGoalList.jsx +0 -51
- package/src/CompetenceGoals/CompetenceGoals.jsx +0 -152
- package/src/CompetenceGoals/component.competence-goals.scss +0 -161
- package/src/CompetenceGoals/index.js +0 -6
- package/src/Filter/FilterList.jsx +0 -167
- package/src/Filter/FilterListPhone.jsx +0 -328
- package/src/Filter/ToggleItem.jsx +0 -71
- package/src/Masthead/component.masthead.scss +0 -146
- package/src/SectionHeading/component.section-heading.scss +0 -17
- package/src/Subject/SubjectFilter.tsx +0 -48
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2016-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
|
+
* FRI OG BEGRENSET
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React, { ChangeEvent, useEffect, useState } from 'react';
|
|
10
|
+
import { ChevronDown, ChevronUp } from '@ndla/icons/common';
|
|
11
|
+
import Modal, { ModalHeader, ModalBody, ModalCloseButton } from '@ndla/modal';
|
|
12
|
+
import Button from '@ndla/button';
|
|
13
|
+
import { debounce } from 'lodash';
|
|
14
|
+
import { classes } from './filterClasses';
|
|
15
|
+
import ToggleItem from './ToggleItem';
|
|
16
|
+
import ActiveFilters from '../Search/ActiveFilters';
|
|
17
|
+
|
|
18
|
+
interface Option {
|
|
19
|
+
title: string;
|
|
20
|
+
value: string;
|
|
21
|
+
noResults?: boolean;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface Props {
|
|
26
|
+
preid: string;
|
|
27
|
+
label?: string;
|
|
28
|
+
labelNotVisible?: boolean;
|
|
29
|
+
modifiers?: string;
|
|
30
|
+
onChange: (values: string[], value: string) => void;
|
|
31
|
+
options: Option[] | Option[][];
|
|
32
|
+
values?: string[];
|
|
33
|
+
defaultVisibleCount?: number;
|
|
34
|
+
showLabel?: string;
|
|
35
|
+
hideLabel?: string;
|
|
36
|
+
alignedGroup?: boolean;
|
|
37
|
+
collapseMobile?: boolean;
|
|
38
|
+
activeFiltersNarrow?: boolean;
|
|
39
|
+
messages: {
|
|
40
|
+
useFilter: string;
|
|
41
|
+
openFilter: string;
|
|
42
|
+
closeFilter: string;
|
|
43
|
+
};
|
|
44
|
+
viewMode?: 'inlineDesktop' | 'allModal';
|
|
45
|
+
isGroupedOptions?: boolean;
|
|
46
|
+
showActiveFiltersOnSmallScreen?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const is2DArray = (options: Option[] | Option[][]): options is Option[][] => {
|
|
50
|
+
return Array.isArray(options[0]);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const FilterListPhone = ({
|
|
54
|
+
preid,
|
|
55
|
+
label,
|
|
56
|
+
labelNotVisible,
|
|
57
|
+
modifiers = '',
|
|
58
|
+
onChange,
|
|
59
|
+
options,
|
|
60
|
+
values = [],
|
|
61
|
+
defaultVisibleCount,
|
|
62
|
+
showLabel,
|
|
63
|
+
hideLabel,
|
|
64
|
+
alignedGroup = false,
|
|
65
|
+
collapseMobile = true,
|
|
66
|
+
activeFiltersNarrow,
|
|
67
|
+
messages,
|
|
68
|
+
viewMode = 'inlineDesktop',
|
|
69
|
+
isGroupedOptions,
|
|
70
|
+
showActiveFiltersOnSmallScreen,
|
|
71
|
+
}: Props) => {
|
|
72
|
+
const [isNarrowScreen, setIsNarrowScreen] = useState(false);
|
|
73
|
+
const [visibleCount, setVisibleCount] = useState(defaultVisibleCount);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
setScreenSize(true);
|
|
77
|
+
const debounced = debounce(() => setScreenSize(false), 50);
|
|
78
|
+
window.addEventListener('resize', debounced);
|
|
79
|
+
|
|
80
|
+
return () => {
|
|
81
|
+
debounced.cancel();
|
|
82
|
+
window.removeEventListener('resize', debounced);
|
|
83
|
+
};
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
const setScreenSize = (initial = false) => {
|
|
87
|
+
const newIsNarrowScreen = (window.innerWidth || document.documentElement.clientWidth) < 768;
|
|
88
|
+
|
|
89
|
+
/* eslint react/no-did-mount-set-state: 0 */
|
|
90
|
+
if ((initial && newIsNarrowScreen) || !initial) {
|
|
91
|
+
setIsNarrowScreen(newIsNarrowScreen);
|
|
92
|
+
}
|
|
93
|
+
/* eslint react/no-did-mount-set-state: 1 */
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleChange = (event: ChangeEvent<HTMLInputElement>, option: Option) => {
|
|
97
|
+
let newValues = null;
|
|
98
|
+
if (event.currentTarget.checked) {
|
|
99
|
+
newValues = [...values, option.value];
|
|
100
|
+
} else {
|
|
101
|
+
newValues = values.filter((value) => value !== option.value);
|
|
102
|
+
}
|
|
103
|
+
if (onChange) {
|
|
104
|
+
onChange(newValues, option.value);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const showAll = defaultVisibleCount === undefined || options.length <= defaultVisibleCount;
|
|
109
|
+
const labelModifiers: string[] = [];
|
|
110
|
+
|
|
111
|
+
if (labelNotVisible) {
|
|
112
|
+
labelModifiers.push('hidden');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const groupedOptions = is2DArray(options) ? options : [options];
|
|
116
|
+
|
|
117
|
+
if (isNarrowScreen || viewMode === 'allModal') {
|
|
118
|
+
let currentlyActiveFilters: Option[] = [];
|
|
119
|
+
groupedOptions.forEach((options) => {
|
|
120
|
+
const activeFilters = options.filter((option) => values.some((value) => value === option.value));
|
|
121
|
+
currentlyActiveFilters = [...currentlyActiveFilters, ...activeFilters];
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const wrapperClassName =
|
|
125
|
+
activeFiltersNarrow || viewMode === 'allModal' ? classes('narrow-active-filters').className : '';
|
|
126
|
+
return (
|
|
127
|
+
<div className={wrapperClassName}>
|
|
128
|
+
{currentlyActiveFilters.length > 0 && (
|
|
129
|
+
<ActiveFilters
|
|
130
|
+
filters={currentlyActiveFilters}
|
|
131
|
+
showOnSmallScreen={showActiveFiltersOnSmallScreen}
|
|
132
|
+
onFilterRemove={(value) => {
|
|
133
|
+
onChange(
|
|
134
|
+
values.filter((option) => option !== value),
|
|
135
|
+
value,
|
|
136
|
+
);
|
|
137
|
+
}}
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
<Modal
|
|
141
|
+
size="fullscreen"
|
|
142
|
+
backgroundColor="grey"
|
|
143
|
+
activateButton={
|
|
144
|
+
<Button outline {...classes('modal-button')}>
|
|
145
|
+
{messages.openFilter}
|
|
146
|
+
</Button>
|
|
147
|
+
}>
|
|
148
|
+
{(onClose) => (
|
|
149
|
+
<>
|
|
150
|
+
<ModalHeader modifier={['left-align']}>
|
|
151
|
+
<div {...classes('modal-header')}>
|
|
152
|
+
<div {...classes('modal-heading')}>
|
|
153
|
+
{!isNarrowScreen && label && <h1 {...classes('label')}>{label}</h1>}
|
|
154
|
+
<Button outline onClick={onClose}>
|
|
155
|
+
{messages.useFilter}
|
|
156
|
+
</Button>
|
|
157
|
+
</div>
|
|
158
|
+
<ModalCloseButton title={messages.closeFilter} onClick={onClose} />
|
|
159
|
+
</div>
|
|
160
|
+
</ModalHeader>
|
|
161
|
+
<ModalBody modifier="no-side-padding-mobile">
|
|
162
|
+
{isNarrowScreen && label && <h1 {...classes('label')}>{label}</h1>}
|
|
163
|
+
{groupedOptions.map((options, index) => (
|
|
164
|
+
<ul
|
|
165
|
+
key={index}
|
|
166
|
+
{...classes('item-wrapper', {
|
|
167
|
+
'aligned-grouping': !!alignedGroup,
|
|
168
|
+
'collapse-mobile': !!collapseMobile,
|
|
169
|
+
'grouped-options': !!isGroupedOptions,
|
|
170
|
+
})}>
|
|
171
|
+
{options.map((option) => {
|
|
172
|
+
const itemModifiers = [];
|
|
173
|
+
|
|
174
|
+
const checked = values.some((value) => value === option.value);
|
|
175
|
+
|
|
176
|
+
if (option.noResults) {
|
|
177
|
+
itemModifiers.push('no-results');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (option.disabled) {
|
|
181
|
+
itemModifiers.push('disabled');
|
|
182
|
+
}
|
|
183
|
+
return (
|
|
184
|
+
<ToggleItem
|
|
185
|
+
key={option.value}
|
|
186
|
+
id={preid + option.value}
|
|
187
|
+
value={option.value}
|
|
188
|
+
checked={checked}
|
|
189
|
+
onChange={(event) => handleChange(event, option)}
|
|
190
|
+
label={option.title}
|
|
191
|
+
disabled={option.disabled}
|
|
192
|
+
modifiers={itemModifiers}
|
|
193
|
+
/>
|
|
194
|
+
);
|
|
195
|
+
})}
|
|
196
|
+
</ul>
|
|
197
|
+
))}
|
|
198
|
+
|
|
199
|
+
<div {...classes('usefilter-wrapper')}>
|
|
200
|
+
<Button outline onClick={onClose}>
|
|
201
|
+
{messages.useFilter}
|
|
202
|
+
</Button>
|
|
203
|
+
</div>
|
|
204
|
+
</ModalBody>
|
|
205
|
+
</>
|
|
206
|
+
)}
|
|
207
|
+
</Modal>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<>
|
|
214
|
+
{isGroupedOptions && label && <h2 {...classes('label', labelModifiers)}>{label}</h2>}
|
|
215
|
+
{groupedOptions.map((options, index) => (
|
|
216
|
+
<section key={index} {...classes('list', modifiers)}>
|
|
217
|
+
{!isGroupedOptions && label && <h1 {...classes('label', labelModifiers)}>{label}</h1>}
|
|
218
|
+
<ul {...classes('item-wrapper')}>
|
|
219
|
+
{options.map((option, index) => {
|
|
220
|
+
const itemModifiers = [];
|
|
221
|
+
|
|
222
|
+
const checked = values.some((value) => value === option.value);
|
|
223
|
+
|
|
224
|
+
if (!showAll && !checked && !!visibleCount && index + 1 > visibleCount) {
|
|
225
|
+
itemModifiers.push('hidden');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (option.noResults) {
|
|
229
|
+
itemModifiers.push('no-results');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (option.disabled) {
|
|
233
|
+
itemModifiers.push('disabled');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<ToggleItem
|
|
238
|
+
key={option.value}
|
|
239
|
+
id={preid + option.value}
|
|
240
|
+
value={option.value}
|
|
241
|
+
tabIndex={option.noResults ? -1 : 0}
|
|
242
|
+
checked={checked}
|
|
243
|
+
onChange={(event) => handleChange(event, option)}
|
|
244
|
+
label={option.title}
|
|
245
|
+
modifiers={itemModifiers}
|
|
246
|
+
disabled={option.disabled}
|
|
247
|
+
/>
|
|
248
|
+
);
|
|
249
|
+
})}
|
|
250
|
+
</ul>
|
|
251
|
+
{!showAll && (
|
|
252
|
+
<button
|
|
253
|
+
{...classes('expand')}
|
|
254
|
+
type="button"
|
|
255
|
+
onClick={() => {
|
|
256
|
+
setVisibleCount((prev) => (prev === defaultVisibleCount ? options.length : defaultVisibleCount));
|
|
257
|
+
}}>
|
|
258
|
+
{visibleCount === defaultVisibleCount ? (
|
|
259
|
+
<>
|
|
260
|
+
<span>{showLabel}</span> <ChevronDown />
|
|
261
|
+
</>
|
|
262
|
+
) : (
|
|
263
|
+
<>
|
|
264
|
+
<span>{hideLabel}</span> <ChevronUp />
|
|
265
|
+
</>
|
|
266
|
+
)}
|
|
267
|
+
</button>
|
|
268
|
+
)}
|
|
269
|
+
</section>
|
|
270
|
+
))}
|
|
271
|
+
</>
|
|
272
|
+
);
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
export default FilterListPhone;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2017-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, { ChangeEvent, ElementType } from 'react';
|
|
10
|
+
import { classes } from './filterClasses';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
id: string;
|
|
14
|
+
label: string;
|
|
15
|
+
onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
16
|
+
checked?: boolean;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
tabIndex?: number;
|
|
19
|
+
modifiers?: string[] | string;
|
|
20
|
+
value?: string;
|
|
21
|
+
component?: ElementType;
|
|
22
|
+
hits?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ToggleItem = ({
|
|
26
|
+
id,
|
|
27
|
+
checked = false,
|
|
28
|
+
modifiers,
|
|
29
|
+
label,
|
|
30
|
+
component: Component = 'li',
|
|
31
|
+
onChange,
|
|
32
|
+
tabIndex,
|
|
33
|
+
value,
|
|
34
|
+
disabled,
|
|
35
|
+
hits,
|
|
36
|
+
}: Props) => (
|
|
37
|
+
<Component {...classes('item', modifiers)}>
|
|
38
|
+
<input
|
|
39
|
+
{...classes('input')}
|
|
40
|
+
type="checkbox"
|
|
41
|
+
id={id}
|
|
42
|
+
value={value}
|
|
43
|
+
disabled={disabled}
|
|
44
|
+
tabIndex={tabIndex}
|
|
45
|
+
checked={checked}
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
/>
|
|
48
|
+
<label htmlFor={id}>
|
|
49
|
+
<span {...classes('item-checkbox')} />
|
|
50
|
+
<span {...classes('text')}>
|
|
51
|
+
{label}
|
|
52
|
+
{hits !== undefined && ` (${hits})`}
|
|
53
|
+
</span>
|
|
54
|
+
</label>
|
|
55
|
+
</Component>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
export default ToggleItem;
|
|
File without changes
|
|
File without changes
|
|
@@ -5,7 +5,6 @@ import Tabs from '@ndla/tabs';
|
|
|
5
5
|
import SafeLink from '@ndla/safelink';
|
|
6
6
|
import { colors, fonts, mq, breakpoints } from '@ndla/core';
|
|
7
7
|
import { MessageBox } from '../Messages';
|
|
8
|
-
// @ts-ignore
|
|
9
8
|
import { ToggleItem } from '../Filter';
|
|
10
9
|
|
|
11
10
|
const StyledWrapper = styled.nav`
|
|
@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
|
|
|
4
4
|
import { colors, spacing, mq, breakpoints, animations } from '@ndla/core';
|
|
5
5
|
import { noScroll } from '@ndla/util';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
-
// @ts-ignore
|
|
8
7
|
import { SearchField } from '../Search';
|
|
9
8
|
import { SearchFieldForm } from '../Search/SearchFieldForm';
|
|
10
9
|
import SearchResultSleeve from '../Search/SearchResultSleeve';
|
|
@@ -6,39 +6,98 @@
|
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { css } from '@emotion/core';
|
|
10
|
+
import styled from '@emotion/styled';
|
|
11
|
+
import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core/src';
|
|
9
12
|
import React, { ReactNode, useEffect, useRef } from 'react';
|
|
10
|
-
import BEMHelper from 'react-bem-helper';
|
|
11
|
-
import { WithTranslation, withTranslation } from 'react-i18next';
|
|
12
|
-
import { DisplayOnPageYOffset } from '../Animation';
|
|
13
13
|
import { MessageBanner } from '../Messages';
|
|
14
|
-
|
|
15
|
-
const classes = new BEMHelper({
|
|
16
|
-
name: 'masthead',
|
|
17
|
-
prefix: 'c-',
|
|
18
|
-
});
|
|
14
|
+
import SkipToMainContent from './SkipToMainContent';
|
|
19
15
|
|
|
20
16
|
interface MastheadItemProps {
|
|
21
17
|
children?: ReactNode;
|
|
22
|
-
className?: string;
|
|
23
18
|
right?: boolean;
|
|
24
19
|
left?: boolean;
|
|
25
20
|
}
|
|
26
|
-
export const MastheadItem = ({ children, className, left = false, right = false }: MastheadItemProps) => {
|
|
27
|
-
const itemClassName = left ? 'left' : right ? 'right' : undefined;
|
|
28
|
-
const itemClassNames = itemClassName ? classes(itemClassName).className : undefined;
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
const LeftMastheadItem = styled.div`
|
|
23
|
+
display: flex;
|
|
24
|
+
button {
|
|
25
|
+
white-space: nowrap;
|
|
26
|
+
}
|
|
27
|
+
> div:last-child {
|
|
28
|
+
flex-grow: 1;
|
|
29
|
+
}
|
|
30
|
+
${mq.range({ from: breakpoints.desktop })} {
|
|
31
|
+
flex-grow: 1;
|
|
32
|
+
text-align: left;
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const RightMastheadItem = styled.div`
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: flex-end;
|
|
40
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
41
|
+
padding: ${spacing.small} 0;
|
|
42
|
+
padding: 0;
|
|
43
|
+
}
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
export const MastheadItem = ({ children, left = false, right = false }: MastheadItemProps) => {
|
|
47
|
+
const Wrapper = left ? LeftMastheadItem : right ? RightMastheadItem : 'div';
|
|
48
|
+
return <Wrapper>{children}</Wrapper>;
|
|
31
49
|
};
|
|
32
50
|
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
const MastheadContent = styled.div`
|
|
52
|
+
justify-content: center;
|
|
53
|
+
align-items: center;
|
|
54
|
+
text-align: center;
|
|
55
|
+
padding: ${spacing.small};
|
|
56
|
+
font-weight: ${fonts.weight.normal};
|
|
57
|
+
display: flex;
|
|
58
|
+
height: 84px;
|
|
59
|
+
justify-content: space-between;
|
|
60
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
61
|
+
flex-direction: row;
|
|
62
|
+
padding: ${spacing.small} ${spacing.normal};
|
|
63
|
+
}
|
|
64
|
+
${mq.range({ from: breakpoints.desktop })} {
|
|
65
|
+
padding: ${spacing.small} ${spacing.large};
|
|
66
|
+
}
|
|
67
|
+
`;
|
|
68
|
+
|
|
69
|
+
interface StyledMastheadProps {
|
|
70
|
+
fixed: boolean;
|
|
71
|
+
ndlaFilm: boolean;
|
|
35
72
|
}
|
|
36
73
|
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
74
|
+
const StyledMasthead = styled.div<StyledMastheadProps>`
|
|
75
|
+
z-index: 99;
|
|
76
|
+
position: relative;
|
|
77
|
+
background: white;
|
|
78
|
+
border-bottom: 1px solid ${colors.brand.greyLighter};
|
|
79
|
+
min-height: 84px;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-flow: column;
|
|
82
|
+
justify-content: flex-end;
|
|
83
|
+
${(p) =>
|
|
84
|
+
p.fixed &&
|
|
85
|
+
css`
|
|
86
|
+
top: 0;
|
|
87
|
+
position: sticky;
|
|
88
|
+
@media print {
|
|
89
|
+
position: relative;
|
|
90
|
+
}
|
|
91
|
+
`};
|
|
92
|
+
${(p) =>
|
|
93
|
+
p.ndlaFilm &&
|
|
94
|
+
css`
|
|
95
|
+
background: ${colors.ndlaFilm.filmColorLight};
|
|
96
|
+
background-image: linear-gradient(0deg, ${colors.ndlaFilm.filmColorLight}, ${colors.ndlaFilm.filmColor});
|
|
97
|
+
border: 0;
|
|
98
|
+
border-bottom: 1px solid #18334c;
|
|
99
|
+
`};
|
|
100
|
+
`;
|
|
42
101
|
|
|
43
102
|
interface Alert {
|
|
44
103
|
content: string;
|
|
@@ -49,23 +108,13 @@ interface Alert {
|
|
|
49
108
|
interface Props {
|
|
50
109
|
children?: ReactNode;
|
|
51
110
|
fixed?: boolean;
|
|
52
|
-
infoContent?: ReactNode;
|
|
53
111
|
ndlaFilm?: boolean;
|
|
54
112
|
skipToMainContentId?: string;
|
|
55
113
|
messages?: Alert[];
|
|
56
114
|
onCloseAlert?: (id: number) => void;
|
|
57
115
|
}
|
|
58
116
|
|
|
59
|
-
export const Masthead = ({
|
|
60
|
-
children,
|
|
61
|
-
fixed,
|
|
62
|
-
infoContent,
|
|
63
|
-
ndlaFilm,
|
|
64
|
-
skipToMainContentId,
|
|
65
|
-
messages,
|
|
66
|
-
onCloseAlert,
|
|
67
|
-
t,
|
|
68
|
-
}: Props & WithTranslation) => {
|
|
117
|
+
export const Masthead = ({ children, fixed, ndlaFilm, skipToMainContentId, messages, onCloseAlert }: Props) => {
|
|
69
118
|
const mastheadRef = useRef<HTMLDivElement>(null);
|
|
70
119
|
const focusHandler = (evt: FocusEvent) => {
|
|
71
120
|
const mastheadHeight = (mastheadRef.current && mastheadRef.current.offsetHeight) || 0;
|
|
@@ -88,12 +137,8 @@ export const Masthead = ({
|
|
|
88
137
|
|
|
89
138
|
return (
|
|
90
139
|
<>
|
|
91
|
-
{skipToMainContentId &&
|
|
92
|
-
|
|
93
|
-
{t('masthead.skipToContent')}
|
|
94
|
-
</a>
|
|
95
|
-
)}
|
|
96
|
-
<div id="masthead" {...classes('', { fixed: !!fixed, infoContent: !!infoContent, ndlaFilm: !!ndlaFilm })}>
|
|
140
|
+
{skipToMainContentId && <SkipToMainContent skipToMainContentId={skipToMainContentId} />}
|
|
141
|
+
<StyledMasthead fixed={!!fixed} ndlaFilm={!!ndlaFilm} id="masthead">
|
|
97
142
|
{messages?.map((message) => (
|
|
98
143
|
<MessageBanner
|
|
99
144
|
key={message.number}
|
|
@@ -102,15 +147,10 @@ export const Masthead = ({
|
|
|
102
147
|
{message.content}
|
|
103
148
|
</MessageBanner>
|
|
104
149
|
))}
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
<MastheadInfo>{infoContent}</MastheadInfo>
|
|
108
|
-
</DisplayOnPageYOffset>
|
|
109
|
-
)}
|
|
110
|
-
<div className={`u-1/1 ${classes('content').className}`}>{children}</div>
|
|
111
|
-
</div>
|
|
150
|
+
<MastheadContent className="u-1/1">{children}</MastheadContent>
|
|
151
|
+
</StyledMasthead>
|
|
112
152
|
</>
|
|
113
153
|
);
|
|
114
154
|
};
|
|
115
155
|
|
|
116
|
-
export default
|
|
156
|
+
export default Masthead;
|
|
@@ -7,7 +7,6 @@ import styled from '@emotion/styled';
|
|
|
7
7
|
import { css } from '@emotion/core';
|
|
8
8
|
import { spacing, mq, breakpoints, colors, shadows } from '@ndla/core';
|
|
9
9
|
import { WithTranslation, withTranslation } from 'react-i18next';
|
|
10
|
-
// @ts-ignore
|
|
11
10
|
import ToggleSearchButton from '../Search/ToggleSearchButton';
|
|
12
11
|
|
|
13
12
|
interface Props {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import styled from '@emotion/styled';
|
|
4
|
+
import { colors } from '@ndla/core';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
skipToMainContentId: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const StyledSkipToMainContent = styled.a`
|
|
11
|
+
left: -999px;
|
|
12
|
+
position: absolute;
|
|
13
|
+
top: auto;
|
|
14
|
+
width: 1px;
|
|
15
|
+
height: 1px;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
z-index: -999;
|
|
18
|
+
&:focus,
|
|
19
|
+
&:active {
|
|
20
|
+
color: ${colors.white};
|
|
21
|
+
background: ${colors.brand.primary};
|
|
22
|
+
left: auto;
|
|
23
|
+
top: auto;
|
|
24
|
+
width: 30%;
|
|
25
|
+
height: auto;
|
|
26
|
+
overflow: auto;
|
|
27
|
+
margin: 10px 35%;
|
|
28
|
+
padding: 5px;
|
|
29
|
+
border-radius: 15px;
|
|
30
|
+
border: 4px solid ${colors.brand.tertiary};
|
|
31
|
+
text-align: center;
|
|
32
|
+
font-size: 1.2em;
|
|
33
|
+
z-index: 9999;
|
|
34
|
+
animation-name: fadeIn;
|
|
35
|
+
animation-duration: 0.3s;
|
|
36
|
+
}
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const SkipToMainContent = ({ skipToMainContentId }: Props) => {
|
|
40
|
+
const { t } = useTranslation();
|
|
41
|
+
return (
|
|
42
|
+
<StyledSkipToMainContent tabIndex={0} href={`#${skipToMainContentId}`}>
|
|
43
|
+
{t('masthead.skipToContent')}
|
|
44
|
+
</StyledSkipToMainContent>
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default SkipToMainContent;
|
package/src/Masthead/index.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
import Masthead, { MastheadItem } from './Masthead';
|
|
10
10
|
|
|
11
11
|
import { getMastheadHeight, useMastheadHeight } from './utils';
|
|
12
|
+
import { default as SkipToMainContent } from './SkipToMainContent';
|
|
12
13
|
|
|
13
|
-
export { MastheadItem, getMastheadHeight, useMastheadHeight };
|
|
14
|
+
export { MastheadItem, getMastheadHeight, useMastheadHeight, SkipToMainContent };
|
|
14
15
|
|
|
15
16
|
export default Masthead;
|
|
@@ -12,7 +12,6 @@ import Modal, { ModalCloseButton, ModalBody } from '@ndla/modal';
|
|
|
12
12
|
import { breakpoints, fonts, mq, spacing } from '@ndla/core';
|
|
13
13
|
import Button from '@ndla/button';
|
|
14
14
|
import { useTranslation } from 'react-i18next';
|
|
15
|
-
// @ts-ignore
|
|
16
15
|
import { ToggleItem } from '../Filter';
|
|
17
16
|
|
|
18
17
|
import FrontpageAllSubjects, { subjectsProps } from '../Frontpage/FrontpageAllSubjects';
|
|
@@ -12,7 +12,6 @@ import styled from '@emotion/styled';
|
|
|
12
12
|
import { spacing } from '@ndla/core';
|
|
13
13
|
|
|
14
14
|
import SearchViewType, { SearchViewTypeProps } from './SearchViewType';
|
|
15
|
-
// @ts-ignore
|
|
16
15
|
import { FilterButtons } from '../Filter';
|
|
17
16
|
import { FilterButtonsProps } from '../Filter/FilterButtons';
|
|
18
17
|
|
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
import React, { ElementType, ReactNode } from 'react';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { css } from '@emotion/core';
|
|
3
|
+
import { breakpoints, fonts, mq, spacing } from '@ndla/core/src';
|
|
4
|
+
import styled from '@emotion/styled';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
interface StyledWrapperProps {
|
|
7
|
+
large?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const StyledWrapper = styled.h2<StyledWrapperProps>`
|
|
11
|
+
font-weight: ${fonts.weight.bold};
|
|
12
|
+
text-transform: uppercase;
|
|
13
|
+
letter-spacing: 0.05em;
|
|
14
|
+
${fonts.sizes('18px', '24px')};
|
|
15
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
16
|
+
${fonts.sizes('20px', '26px')};
|
|
17
|
+
}
|
|
18
|
+
${(p) =>
|
|
19
|
+
p.large &&
|
|
20
|
+
css`
|
|
21
|
+
margin: 0 0 ${spacing.small} 0;
|
|
22
|
+
${fonts.sizes('16px', '32px')};
|
|
23
|
+
${mq.range({ from: breakpoints.tablet })} {
|
|
24
|
+
${fonts.sizes('22px')};
|
|
25
|
+
}
|
|
26
|
+
`};
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const LargeStyledWrapper = StyledWrapper.withComponent('h2');
|
|
6
30
|
|
|
7
31
|
interface Props {
|
|
8
32
|
children: ReactNode;
|
|
@@ -11,19 +35,8 @@ interface Props {
|
|
|
11
35
|
}
|
|
12
36
|
|
|
13
37
|
const SectionHeading = ({ children, large = false, className }: Props) => {
|
|
14
|
-
const Wrapper: ElementType = large ?
|
|
15
|
-
return <Wrapper {
|
|
38
|
+
const Wrapper: ElementType = large ? LargeStyledWrapper : StyledWrapper;
|
|
39
|
+
return <Wrapper className={className}>{children}</Wrapper>;
|
|
16
40
|
};
|
|
17
41
|
|
|
18
42
|
export default SectionHeading;
|
|
19
|
-
|
|
20
|
-
SectionHeading.propTypes = {
|
|
21
|
-
children: PropTypes.node.isRequired,
|
|
22
|
-
large: PropTypes.bool,
|
|
23
|
-
className: PropTypes.string,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
SectionHeading.defaultProps = {
|
|
27
|
-
large: false,
|
|
28
|
-
className: null,
|
|
29
|
-
};
|
package/src/Subject/index.ts
CHANGED
|
@@ -4,7 +4,6 @@ export { default as SubjectLinks } from './SubjectLinks';
|
|
|
4
4
|
export { default as SubjectArchive } from './SubjectArchive';
|
|
5
5
|
export { default as SubjectAbout } from './SubjectAbout';
|
|
6
6
|
export { default as SubjectCarousel } from './SubjectCarousel';
|
|
7
|
-
export { default as SubjectFilter } from './SubjectFilter';
|
|
8
7
|
export { default as SubjectNewContent } from './SubjectNewContent';
|
|
9
8
|
export { default as SubjectBanner } from './SubjectBanner';
|
|
10
9
|
|