@ndla/ui 16.0.0 → 16.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.
Files changed (37) hide show
  1. package/es/NDLAFilm/CategorySelect.js +51 -25
  2. package/es/NDLAFilm/FilmMovieList.js +14 -10
  3. package/es/NDLAFilm/FilmMovieSearch.js +23 -10
  4. package/es/NDLAFilm/FilmSlideshow.js +117 -19
  5. package/es/NDLAFilm/MovieGrid.js +23 -14
  6. package/es/NDLAFilm/NavigationArrow.js +33 -7
  7. package/es/NDLAFilm/SlideshowIndicator.js +27 -11
  8. package/es/NDLAFilm/filmStyles.js +23 -0
  9. package/es/RadioButtonGroup/RadioButtonGroup.js +28 -9
  10. package/es/all.css +1 -1
  11. package/lib/NDLAFilm/CategorySelect.js +52 -30
  12. package/lib/NDLAFilm/FilmMovieList.js +17 -13
  13. package/lib/NDLAFilm/FilmMovieSearch.js +29 -17
  14. package/lib/NDLAFilm/FilmSlideshow.js +113 -24
  15. package/lib/NDLAFilm/MovieGrid.js +25 -15
  16. package/lib/NDLAFilm/NavigationArrow.d.ts +5 -1
  17. package/lib/NDLAFilm/NavigationArrow.js +34 -10
  18. package/lib/NDLAFilm/SlideshowIndicator.js +34 -13
  19. package/lib/NDLAFilm/filmStyles.d.ts +8 -0
  20. package/lib/NDLAFilm/filmStyles.js +38 -0
  21. package/lib/RadioButtonGroup/RadioButtonGroup.js +28 -13
  22. package/lib/all.css +1 -1
  23. package/package.json +2 -2
  24. package/src/NDLAFilm/CategorySelect.tsx +110 -23
  25. package/src/NDLAFilm/FilmMovieList.tsx +13 -11
  26. package/src/NDLAFilm/FilmMovieSearch.tsx +45 -14
  27. package/src/NDLAFilm/FilmSlideshow.tsx +186 -25
  28. package/src/NDLAFilm/MovieGrid.tsx +33 -25
  29. package/src/NDLAFilm/NavigationArrow.tsx +76 -10
  30. package/src/NDLAFilm/SlideshowIndicator.tsx +53 -11
  31. package/src/NDLAFilm/component.film-movielist.scss +0 -46
  32. package/src/NDLAFilm/filmStyles.ts +33 -0
  33. package/src/RadioButtonGroup/RadioButtonGroup.tsx +82 -11
  34. package/src/main.scss +0 -3
  35. package/src/NDLAFilm/component.film-moviesearch.scss +0 -127
  36. package/src/NDLAFilm/component.film-slideshow.scss +0 -258
  37. package/src/RadioButtonGroup/component.radio-button-group.scss +0 -67
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndla/ui",
3
- "version": "16.0.0",
3
+ "version": "16.1.0",
4
4
  "description": "UI component library for NDLA.",
5
5
  "license": "GPL-3.0",
6
6
  "main": "lib/index.js",
@@ -81,5 +81,5 @@
81
81
  "publishConfig": {
82
82
  "access": "public"
83
83
  },
84
- "gitHead": "765457c9aae617b16a3a17269f4e9401f70057cc"
84
+ "gitHead": "d0828e0cc8cef62893e54aab44b37f6459438e9b"
85
85
  }
@@ -1,14 +1,109 @@
1
1
  import React, { Component } from 'react';
2
- import BEMHelper from 'react-bem-helper';
3
2
  import FocusTrapReact from 'focus-trap-react';
3
+ import styled from '@emotion/styled';
4
+ import { keyframes } from '@emotion/core';
5
+ import { breakpoints, colors, fonts, mq, spacing } from '@ndla/core';
4
6
  import { ChevronDown } from '@ndla/icons/common';
5
7
  import { WithTranslation, withTranslation } from 'react-i18next';
6
8
  import { MovieResourceType } from './types';
7
9
 
8
- const classes = new BEMHelper({
9
- name: 'film-moviesearch',
10
- prefix: 'c-',
11
- });
10
+ const zoomInSelect = keyframes`
11
+ 0% {
12
+ display: none;
13
+ opacity: 0;
14
+ }
15
+ 1% {
16
+ display: flex;
17
+ transform: scale3d(0.95, 0.95, 0.95);
18
+ opacity: 0;
19
+ }
20
+ 100% {
21
+ opacity: 1;
22
+ transform: scale3d(1, 1, 1);
23
+ }
24
+ `;
25
+
26
+ const DropdownContainer = styled.div`
27
+ position: relative;
28
+ margin: 0 0 ${spacing.normal};
29
+ `;
30
+
31
+ const DropdownButton = styled.button`
32
+ border-radius: 4px;
33
+ ${fonts.sizes('22px', '26px')};
34
+ border: 0;
35
+ text-transform: uppercase;
36
+ background: ${colors.ndlaFilm.filmColorBright};
37
+ color: ${colors.white};
38
+ padding: ${spacing.small} ${spacing.nsmall};
39
+ font-weight: ${fonts.weight.semibold};
40
+ display: flex;
41
+ align-items: center;
42
+ text-align: left;
43
+ justify-content: space-between;
44
+ width: 100%;
45
+ letter-spacing: 0.05em;
46
+ ${mq.range({ from: breakpoints.tablet })} {
47
+ padding: ${spacing.small} ${spacing.normal};
48
+ }
49
+ div:first-child {
50
+ ${mq.range({ until: breakpoints.tablet })} {
51
+ display: flex;
52
+ flex-direction: column;
53
+ small {
54
+ padding: 0;
55
+ }
56
+ }
57
+ }
58
+ small {
59
+ text-transform: none;
60
+ padding-left: ${spacing.small};
61
+ font-weight: normal;
62
+ }
63
+ `;
64
+
65
+ interface DropdownWrapperProps {
66
+ offsetDropdown: number;
67
+ }
68
+
69
+ const DropdownWrapper = styled.div<DropdownWrapperProps>`
70
+ top: -${(props) => props.offsetDropdown * 52 + 13}px;
71
+ ${DropdownButton} {
72
+ border-radius: 0;
73
+ text-transform: 0;
74
+ letter-spacing: 0;
75
+ }
76
+ display: flex;
77
+ flex-direction: column;
78
+ padding: $spacing--small 0;
79
+ background: ${colors.brand.lighter};
80
+ border-radius: 4px;
81
+ left: 0;
82
+ right: 0;
83
+ animation: ${zoomInSelect} 200ms ease;
84
+ box-shadow: 0 0 30px rgba(${colors.black}, 0.6);
85
+ position: absolute;
86
+ z-index: 9999;
87
+ button,
88
+ a {
89
+ color: ${colors.text.primary};
90
+ border: 0;
91
+ outline: 0;
92
+ text-align: left;
93
+ background: transparent;
94
+ padding: ${spacing.small};
95
+ padding-left: ${spacing.normal};
96
+ font-weight: ${fonts.weight.semibold};
97
+ text-decoration: none;
98
+ box-shadow: none;
99
+ transition: background 200ms ease;
100
+ &:hover,
101
+ &:focus {
102
+ color: ${colors.brand.primary};
103
+ background: rgba(0, 0, 0, 0.2);
104
+ }
105
+ }
106
+ `;
12
107
 
13
108
  interface Props extends WithTranslation {
14
109
  resourceTypes: MovieResourceType[];
@@ -66,12 +161,11 @@ class CategorySelect extends Component<Props, State> {
66
161
  : 0;
67
162
 
68
163
  return (
69
- <div {...classes('dropdown-container', '', 'u-12/12')}>
70
- <button
164
+ <DropdownContainer className="u-12/12">
165
+ <DropdownButton
71
166
  aria-expanded={!resourceTypesIsOpen}
72
167
  aria-controls="selectCategory"
73
168
  type="button"
74
- {...classes('dropdown-button', 'toggleButton')}
75
169
  tabIndex={resourceTypesIsOpen ? -1 : 0}
76
170
  onClick={this.openSelect}>
77
171
  <div>
@@ -83,7 +177,7 @@ class CategorySelect extends Component<Props, State> {
83
177
  <div>
84
178
  <ChevronDown className="c-icon--22" />
85
179
  </div>
86
- </button>
180
+ </DropdownButton>
87
181
  {resourceTypesIsOpen && (
88
182
  <FocusTrapReact
89
183
  active={resourceTypesIsOpen}
@@ -94,32 +188,25 @@ class CategorySelect extends Component<Props, State> {
94
188
  clickOutsideDeactivates: true,
95
189
  escapeDeactivates: true,
96
190
  }}>
97
- <div id="selectCategory" {...classes('dropdown-wrapper')} style={{ top: `-${offsetDropDown * 52 + 13}px` }}>
98
- <button
99
- aria-controls={ariaControlId}
100
- type="button"
101
- onClick={() => this.onSelect()}
102
- {...classes('dropdown-button')}>
191
+ <DropdownWrapper id="selectCategory" offsetDropdown={offsetDropDown}>
192
+ <DropdownButton aria-controls={ariaControlId} type="button" onClick={() => this.onSelect()}>
103
193
  <span>{t('ndlaFilm.search.categoryFromNdla')}</span>
104
- </button>
194
+ </DropdownButton>
105
195
  {resourceTypes.map((resourceType) => (
106
- <button
196
+ <DropdownButton
107
197
  aria-controls={ariaControlId}
108
198
  type="button"
109
199
  ref={(el) => this.createRef(el, resourceType.id)}
110
200
  onClick={() => this.onSelect(resourceType.id)}
111
- {...classes('dropdown-button', {
112
- selected: !!resourceTypeSelected && resourceTypeSelected.id === resourceType.id,
113
- })}
114
201
  data-id={resourceType.id}
115
202
  key={resourceType.id}>
116
203
  <span>{resourceType.name}</span>
117
- </button>
204
+ </DropdownButton>
118
205
  ))}
119
- </div>
206
+ </DropdownWrapper>
120
207
  </FocusTrapReact>
121
208
  )}
122
- </div>
209
+ </DropdownContainer>
123
210
  );
124
211
  }
125
212
  }
@@ -6,15 +6,12 @@
6
6
  */
7
7
 
8
8
  import React from 'react';
9
- import BEMHelper from 'react-bem-helper';
10
9
  import { Carousel, CalculatedCarouselProps } from '@ndla/carousel';
10
+ import styled from '@emotion/styled';
11
+ import { breakpoints, mq, spacing } from '@ndla/core';
11
12
  import FilmContentCard from './FilmContentCard';
12
13
  import { MovieResourceType, MovieType } from './types';
13
-
14
- const classes = new BEMHelper({
15
- name: 'film-movielist',
16
- prefix: 'c-',
17
- });
14
+ import { StyledHeadingH1 } from './filmStyles';
18
15
 
19
16
  interface Props {
20
17
  movies: MovieType[];
@@ -26,6 +23,13 @@ interface Props {
26
23
  resizeThumbnailImages?: boolean;
27
24
  }
28
25
 
26
+ const StyledSection = styled.section`
27
+ margin-bottom: ${spacing.normal};
28
+ ${mq.range({ from: breakpoints.tablet })} {
29
+ margin-bottom: ${spacing.large};
30
+ }
31
+ `;
32
+
29
33
  const FilmMovieList = ({
30
34
  name,
31
35
  movies = [],
@@ -35,10 +39,8 @@ const FilmMovieList = ({
35
39
  autoSizedProps,
36
40
  resizeThumbnailImages,
37
41
  }: Props) => (
38
- <section {...classes()}>
39
- <h1 {...classes('heading')} style={{ marginLeft: `${autoSizedProps.margin}px` }}>
40
- {name}
41
- </h1>
42
+ <StyledSection>
43
+ <StyledHeadingH1 marginLeft={autoSizedProps.margin}>{name}</StyledHeadingH1>
42
44
  <Carousel
43
45
  disableScroll={false}
44
46
  slideBackwardsLabel={slideBackwardsLabel}
@@ -56,7 +58,7 @@ const FilmMovieList = ({
56
58
  ))}
57
59
  {...autoSizedProps}
58
60
  />
59
- </section>
61
+ </StyledSection>
60
62
  );
61
63
 
62
64
  export default FilmMovieList;
@@ -7,22 +7,53 @@
7
7
  */
8
8
 
9
9
  import React from 'react';
10
- import BEMHelper from 'react-bem-helper';
11
10
  import SafeLink from '@ndla/safelink';
12
11
  import { useTranslation } from 'react-i18next';
12
+ import styled from '@emotion/styled';
13
+ import { spacing, mq, breakpoints, colors } from '@ndla/core';
13
14
  import CategorySelect from './CategorySelect';
14
15
  import { MovieResourceType } from './types';
15
16
  import { OneColumn } from '..';
17
+ import { StyledHeadingH2 } from './filmStyles';
16
18
 
17
- const classes = new BEMHelper({
18
- name: 'film-moviesearch',
19
- prefix: 'c-',
20
- });
19
+ const FilmMovieSearchContainer = styled.div`
20
+ margin: ${spacing.normal} 0 ${spacing.large};
21
+ `;
21
22
 
22
- const classesMovieList = new BEMHelper({
23
- name: 'film-movielist',
24
- prefix: 'c-',
25
- });
23
+ const TopicNavigation = styled.div`
24
+ margin: ${spacing.normal} 0;
25
+ ${mq.range({ from: breakpoints.tablet })} {
26
+ display: flex;
27
+ align-items: flex-start;
28
+ padding: 0 ${spacing.normal};
29
+ }
30
+ ul {
31
+ list-style-type: none;
32
+ list-style-image: none;
33
+ display: flex;
34
+ align-items: flex-start;
35
+ flex-wrap: wrap;
36
+ padding: 0;
37
+ margin: ${spacing.small} 0;
38
+ ${mq.range({ from: breakpoints.tablet })} {
39
+ padding-left: ${spacing.normal};
40
+ }
41
+ li {
42
+ padding: 0;
43
+ width: 100%;
44
+ ${mq.range({ from: breakpoints.tablet })} {
45
+ width: 50%;
46
+ }
47
+ a {
48
+ color: #fff;
49
+ &:hover,
50
+ &:focus {
51
+ color: ${colors.brand.light};
52
+ }
53
+ }
54
+ }
55
+ }
56
+ `;
26
57
 
27
58
  interface Props {
28
59
  topics?: { id: string; path: string; name: string }[];
@@ -41,10 +72,10 @@ const FilmMovieSearch = ({
41
72
  }: Props) => {
42
73
  const { t } = useTranslation();
43
74
  return (
44
- <div {...classes('')}>
75
+ <FilmMovieSearchContainer>
45
76
  <OneColumn>
46
- <div {...classes('topic-navigation')}>
47
- <h2 {...classesMovieList('heading', '', 'u-12/12 u-4/12@tablet')}>{t('ndlaFilm.subjectsInMovies')}:</h2>
77
+ <TopicNavigation>
78
+ <StyledHeadingH2 className="u-12/12 u-4/12@tablet">{t('ndlaFilm.subjectsInMovies')}:</StyledHeadingH2>
48
79
  <nav className="u-12/12 u-8/12@tablet">
49
80
  <ul>
50
81
  {topics.map((topic) => (
@@ -56,7 +87,7 @@ const FilmMovieSearch = ({
56
87
  ))}
57
88
  </ul>
58
89
  </nav>
59
- </div>
90
+ </TopicNavigation>
60
91
  <CategorySelect
61
92
  onChangeResourceType={onChangeResourceType}
62
93
  resourceTypes={resourceTypes}
@@ -64,7 +95,7 @@ const FilmMovieSearch = ({
64
95
  ariaControlId={ariaControlId}
65
96
  />
66
97
  </OneColumn>
67
- </div>
98
+ </FilmMovieSearchContainer>
68
99
  );
69
100
  };
70
101
 
@@ -8,11 +8,13 @@
8
8
 
9
9
  import React, { useCallback, useEffect, useRef, useState } from 'react';
10
10
  import { SwipeEventData, useSwipeable } from 'react-swipeable';
11
- import BEMHelper from 'react-bem-helper';
11
+ import styled from '@emotion/styled';
12
+ import { css } from '@emotion/core';
13
+ import { breakpoints, mq, spacing, spacingUnit, fonts, colors } from '@ndla/core';
12
14
  import SafeLink from '@ndla/safelink';
13
15
  import { OneColumn } from '../Layout';
14
16
  import Spinner from '../Spinner';
15
- import NavigationArrow from './NavigationArrow';
17
+ import NavigationArrow, { StyledNavigationArrow } from './NavigationArrow';
16
18
  import SlideshowIndicator from './SlideshowIndicator';
17
19
  import { MovieType } from './types';
18
20
 
@@ -23,17 +25,180 @@ interface Props {
23
25
  slideInterval?: number;
24
26
  }
25
27
 
26
- const classes = new BEMHelper({
27
- name: 'film-slideshow',
28
- prefix: 'c-',
29
- });
28
+ const SlideLinkWrapper = styled.div`
29
+ margin: 0 auto;
30
+ display: flex;
31
+ justify-content: flex-start;
32
+ align-items: flex-end;
33
+ position: absolute;
34
+ z-index: 2;
35
+ height: 100vw;
36
+ width: 100%;
37
+ ${mq.range({ from: breakpoints.mobileWide })} {
38
+ height: 100vw;
39
+ }
40
+ ${mq.range({ from: breakpoints.tablet })} {
41
+ height: 75vw;
42
+ }
43
+ ${mq.range({ from: breakpoints.desktop })} {
44
+ height: 55vw;
45
+ }
46
+ ${mq.range({ from: breakpoints.wide })} {
47
+ height: 40vw;
48
+ }
49
+ ${mq.range({ from: breakpoints.ultraWide })} {
50
+ height: 36vw;
51
+ }
52
+ `;
53
+
54
+ const itemWrapperCSS = css`
55
+ display: flex;
56
+ box-shadow: none;
57
+ `;
58
+
59
+ interface SlideshowItemProps {
60
+ fadeOver?: boolean;
61
+ }
62
+
63
+ const SlideshowItem = styled.div<SlideshowItemProps>`
64
+ width: 100vw;
65
+ height: 100vw;
66
+ /* aspect ratios */
67
+ ${mq.range({ from: breakpoints.mobileWide })} {
68
+ height: 100vw;
69
+ }
70
+ ${mq.range({ from: breakpoints.tablet })} {
71
+ height: 75vw;
72
+ }
73
+ ${mq.range({ from: breakpoints.desktop })} {
74
+ height: 55vw;
75
+ }
76
+ ${mq.range({ from: breakpoints.wide })} {
77
+ height: 40vw;
78
+ }
79
+ ${mq.range({ from: breakpoints.ultraWide })} {
80
+ height: 36vw;
81
+ }
82
+ background-color: '#222';
83
+ background-size: cover;
84
+ background-position-x: center;
85
+ background-position-y: center;
86
+ border: 0;
87
+ position: ${(props) => (props.fadeOver ? 'absolute' : 'relative')};
88
+ animation: ${(props) => props.fadeOver && 'fadeIn 400ms ease'};
89
+ z-index: ${(props) => props.fadeOver && 1};
90
+ &:before {
91
+ content: '';
92
+ opacity: 0.4;
93
+ background: #091a2a;
94
+ top: 0;
95
+ left: 0;
96
+ bottom: 0;
97
+ right: 0;
98
+ position: absolute;
99
+ z-index: 1;
100
+ }
101
+ `;
102
+
103
+ interface SlideshowLinkProps {
104
+ out?: boolean;
105
+ }
106
+
107
+ const SlideshowLink = styled(SafeLink)<SlideshowLinkProps>`
108
+ display: flex;
109
+ box-shadow: none;
110
+ transition: all 400ms ease;
111
+ opacity: ${(props) => props.out && 0};
112
+ animation: ${(props) => !props.out && 'fadeInBottomFixed 600ms ease'};
113
+ ${mq.range({ from: breakpoints.mobileWide })} {
114
+ padding-bottom: ${spacing.medium};
115
+ }
116
+ ${mq.range({ from: breakpoints.tablet })} {
117
+ padding-bottom: ${spacing.large};
118
+ }
119
+ ${mq.range({ from: breakpoints.desktop })} {
120
+ padding-bottom: ${spacingUnit * 3}px;
121
+ }
122
+ &:hover {
123
+ h1 {
124
+ text-decoration: underline;
125
+ }
126
+ }
127
+ `;
128
+
129
+ const SlideshowWrapper = styled.section`
130
+ &:hover {
131
+ ${StyledNavigationArrow} {
132
+ opacity: 1;
133
+ transform: translate(0, 0);
134
+ }
135
+ }
136
+ `;
137
+
138
+ const SlideshowInfo = styled.div`
139
+ border: 0;
140
+ background: none;
141
+ background-color: rgba(3, 23, 43, 0.7);
142
+ border-radius: 4px;
143
+ padding: ${spacing.medium} ${spacing.medium} ${spacing.medium} ${spacing.normal};
144
+ margin: 0 -20px;
145
+ width: 100vw;
146
+ ${mq.range({ from: breakpoints.mobileWide })} {
147
+ margin: 0;
148
+ width: 100%;
149
+ padding: ${spacing.medium} ${spacingUnit * 2}px ${spacing.medium} ${spacing.normal};
150
+ }
151
+ h1 {
152
+ ${fonts.sizes('22px', '30px')};
153
+ color: ${colors.white};
154
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
155
+ margin: 0;
156
+ font-weight: ${fonts.weight.semibold};
157
+ ${mq.range({ from: breakpoints.mobileWide })} {
158
+ margin: 0 0 ${spacing.small};
159
+ ${fonts.sizes('26px', '30px')};
160
+ }
161
+ ${mq.range({ from: breakpoints.tablet })} {
162
+ ${fonts.sizes('40px', '44px')};
163
+ }
164
+ ${mq.range({ from: breakpoints.desktop })} {
165
+ ${fonts.sizes('48px', '54px')};
166
+ }
167
+ }
168
+
169
+ p {
170
+ color: ${colors.white};
171
+ display: inline-block;
172
+ margin: 0;
173
+ padding: 0;
174
+ border-radius: 4px;
175
+ ${fonts.sizes('12px', '18px')};
176
+ ${mq.range({ from: breakpoints.mobileWide })} {
177
+ ${fonts.sizes('15px', '20px')};
178
+ }
179
+ ${mq.range({ from: breakpoints.tablet })} {
180
+ ${fonts.sizes('18px', '24px')};
181
+ }
182
+ ${mq.range({ from: breakpoints.wide })} {
183
+ ${fonts.sizes('20px', '32px')};
184
+ }
185
+ }
186
+ `;
187
+
188
+ const EmptySlideshow = styled.div`
189
+ background: rgba(255, 255, 255, 0.08);
190
+ margin-bottom: $spacing--large * 4;
191
+ display: flex;
192
+ align-items: center;
193
+ justify-content: center;
194
+ height: 40vw;
195
+ `;
30
196
 
31
197
  const defaultTransitionSwipeEnd = 'transform 600ms cubic-bezier(0, 0.76, 0.09, 1)';
32
198
  const defaultTransitionText = 'opacity 600ms ease';
33
199
 
34
200
  const renderSlideItem = (slide: MovieType) => (
35
- <div
36
- {...classes('item')}
201
+ <SlideshowItem
37
202
  key={slide.id}
38
203
  role="img"
39
204
  aria-label={(slide.metaImage && slide.metaImage.alt) || ''}
@@ -190,9 +355,9 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
190
355
  if (slideshow.length === 0) {
191
356
  return (
192
357
  <div>
193
- <div {...classes('slideshow')}>
358
+ <EmptySlideshow>
194
359
  <Spinner inverted />
195
- </div>
360
+ </EmptySlideshow>
196
361
  </div>
197
362
  );
198
363
  }
@@ -208,22 +373,18 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
208
373
  const backgroundImage = slideshow[activeSlide].metaImage;
209
374
 
210
375
  return (
211
- <section {...classes('')} {...handlers}>
376
+ <SlideshowWrapper {...handlers}>
212
377
  <>
213
- <div {...classes('slide-link-wrapper')}>
378
+ <SlideLinkWrapper>
214
379
  <OneColumn>
215
- <SafeLink
216
- to={slideshow[activeSlide].path}
217
- {...classes('item-wrapper', 'text', {
218
- out: !animationComplete,
219
- })}>
220
- <div {...classes('slide-info')} ref={slideText}>
380
+ <SlideshowLink to={slideshow[activeSlide].path} out={!animationComplete}>
381
+ <SlideshowInfo ref={slideText}>
221
382
  <h1>{slideshow[activeSlide].title}</h1>
222
383
  <p>{slideshow[activeSlide].metaDescription}</p>
223
- </div>
224
- </SafeLink>
384
+ </SlideshowInfo>
385
+ </SlideshowLink>
225
386
  </OneColumn>
226
- </div>
387
+ </SlideLinkWrapper>
227
388
  <NavigationArrow
228
389
  slideIndexTarget={slideIndexTarget > 0 ? slideIndexTarget - 1 : slideshow.length - 1}
229
390
  gotoSlide={gotoSlide}
@@ -234,8 +395,8 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
234
395
  rightArrow
235
396
  />
236
397
  {!animationComplete && (
237
- <div
238
- {...classes('item', 'fade-over')}
398
+ <SlideshowItem
399
+ fadeOver
239
400
  role="img"
240
401
  onAnimationEnd={onChangedSlide}
241
402
  style={{
@@ -245,7 +406,7 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
245
406
  )}
246
407
  <div
247
408
  ref={slideRef}
248
- {...classes('item-wrapper')}
409
+ css={itemWrapperCSS}
249
410
  onTransitionEnd={onTransitionEnd}
250
411
  style={{
251
412
  width: slideshowWidth,
@@ -257,7 +418,7 @@ const FilmSlideshow = ({ autoSlide = false, slideshow = [], slideInterval = 5000
257
418
  </div>
258
419
  <SlideshowIndicator slideshow={slideshow} activeSlide={activeSlide} gotoSlide={gotoSlide} />
259
420
  </>
260
- </section>
421
+ </SlideshowWrapper>
261
422
  );
262
423
  };
263
424
 
@@ -1,15 +1,37 @@
1
1
  import React from 'react';
2
- import BEMHelper from 'react-bem-helper';
3
- import { css } from '@emotion/core';
2
+ import styled from '@emotion/styled';
3
+ import { spacing } from '@ndla/core/src';
4
4
  import { CalculatedCarouselProps } from '@ndla/carousel';
5
5
  import { useTranslation } from 'react-i18next';
6
6
  import FilmContentCard from './FilmContentCard';
7
7
  import { MovieResourceType, MovieType } from './types';
8
+ import { setAnimations, StyledHeadingH1 } from './filmStyles';
8
9
 
9
- const movieListClasses = new BEMHelper({
10
- name: 'film-movielist',
11
- prefix: 'c-',
12
- });
10
+ interface MovieListingProps {
11
+ marginLeft?: number;
12
+ }
13
+
14
+ const MovieListing = styled.div<MovieListingProps>`
15
+ display: flex;
16
+ flex-wrap: wrap;
17
+ margin: ${spacing.small} 0;
18
+ margin-left: ${(props) => props.marginLeft && `${props.marginLeft}px`};
19
+ ${setAnimations()};
20
+ > div {
21
+ opacity: 0;
22
+ animation-fill-mode: forwards;
23
+ animation-name: fadeIn;
24
+ animation-duration: 300ms;
25
+ }
26
+ `;
27
+
28
+ interface LoadingPlaceholderProps {
29
+ height?: string;
30
+ }
31
+
32
+ const LoadingPlaceholder = styled.div<LoadingPlaceholderProps>`
33
+ height: ${(props) => props.height};
34
+ `;
13
35
 
14
36
  interface Props {
15
37
  autoSizedProps: CalculatedCarouselProps;
@@ -33,30 +55,16 @@ const MovieGrid = ({
33
55
  const { t } = useTranslation();
34
56
  return (
35
57
  <section>
36
- <h1
37
- {...movieListClasses('heading')}
38
- css={css`
39
- margin-left: ${autoSizedProps.margin}px;
40
- `}>
58
+ <StyledHeadingH1 marginLeft={autoSizedProps.margin}>
41
59
  {resourceTypeName && resourceTypeName.name}
42
60
  <small>
43
61
  {fetchingMoviesByType
44
62
  ? t('ndlaFilm.loadingMovies')
45
63
  : `${moviesByType.length} ${t('ndlaFilm.movieMatchInCategory')}`}
46
64
  </small>
47
- </h1>
48
- <div
49
- {...movieListClasses('movie-listing')}
50
- css={css`
51
- margin-left: ${autoSizedProps.margin}px;
52
- `}>
53
- {fetchingMoviesByType && (
54
- <div
55
- css={css`
56
- height: ${loadingPlaceholderHeight};
57
- `}
58
- />
59
- )}
65
+ </StyledHeadingH1>
66
+ <MovieListing marginLeft={autoSizedProps.margin}>
67
+ {fetchingMoviesByType && <LoadingPlaceholder height={loadingPlaceholderHeight} />}
60
68
  {!fetchingMoviesByType &&
61
69
  moviesByType.map((movie) => (
62
70
  <FilmContentCard
@@ -68,7 +76,7 @@ const MovieGrid = ({
68
76
  resizeThumbnailImages={resizeThumbnailImages}
69
77
  />
70
78
  ))}
71
- </div>
79
+ </MovieListing>
72
80
  </section>
73
81
  );
74
82
  };