@wayke-se/components-react 3.5.3 → 3.6.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/dist-node/index.js +622 -127
- package/dist-node/index.js.map +1 -1
- package/dist-src/components/AccessoryCard/index.js +14 -0
- package/dist-src/components/AccessoryCard/wrapper.js +90 -0
- package/dist-src/components/DocumentList/wrapper.js +1 -1
- package/dist-src/components/Gallery/index.js +7 -4
- package/dist-src/components/Gallery/wrapper.js +1 -38
- package/dist-src/components/Icon/index.js +2 -0
- package/dist-src/components/Image/index.js +5 -0
- package/dist-src/components/Link/index.js +28 -0
- package/dist-src/components/NavButton/index.js +42 -0
- package/dist-src/components/OverflowGrid/index.js +56 -66
- package/dist-src/components/OverflowGrid/wrapper.js +144 -0
- package/dist-src/components/PriceBox/index.js +6 -0
- package/dist-src/components/PriceBox/wrapper.js +34 -0
- package/dist-src/layout/theme.js +8 -0
- package/dist-src/layouts/searchItem/Accessories/AccessoryModal.js +51 -0
- package/dist-src/layouts/searchItem/Accessories/index.js +32 -0
- package/dist-src/layouts/searchItem/Related.js +23 -25
- package/dist-src/layouts/searchItem/index.js +3 -1
- package/dist-src/queries/SEARCH_ITEM_BRANCH_QUERY.js +18 -0
- package/dist-src/queries/SEARCH_ITEM_QUERY.js +18 -0
- package/dist-types/@types/codegen/types.d.ts +17 -0
- package/dist-types/components/AccessoryCard/index.d.ts +16 -0
- package/dist-types/components/AccessoryCard/wrapper.d.ts +18 -0
- package/dist-types/components/Gallery/wrapper.d.ts +2 -6
- package/dist-types/components/Icon/index.d.ts +1 -0
- package/dist-types/components/Image/index.d.ts +1 -0
- package/dist-types/components/Link/index.d.ts +7 -0
- package/dist-types/components/NavButton/index.d.ts +3 -0
- package/dist-types/components/OverflowGrid/index.d.ts +12 -3
- package/dist-types/components/OverflowGrid/wrapper.d.ts +18 -0
- package/dist-types/components/PriceBox/index.d.ts +11 -0
- package/dist-types/components/PriceBox/wrapper.d.ts +10 -0
- package/dist-types/layout/theme.d.ts +8 -0
- package/dist-types/layouts/searchItem/Accessories/AccessoryModal.d.ts +8 -0
- package/dist-types/layouts/searchItem/Accessories/index.d.ts +7 -0
- package/dist-web/index.js +535 -122
- package/dist-web/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Wrapper, Image, ImageaPlaceholder, Body, Heading, Title, Branding, Price, Preamble, ReadMoreBtn, } from './wrapper';
|
|
3
|
+
import { IconImage } from '../Icon/index';
|
|
4
|
+
const AccessoryCard = ({ title, image, branding, price, preamble, readMoreCta, }) => (React.createElement(Wrapper, null,
|
|
5
|
+
image ? (React.createElement(Image, { src: image.src, srcSet: image.srcSet, alt: image.alt })) : (React.createElement(ImageaPlaceholder, null,
|
|
6
|
+
React.createElement(IconImage, null))),
|
|
7
|
+
React.createElement(Body, null,
|
|
8
|
+
React.createElement(Heading, null,
|
|
9
|
+
React.createElement(Title, null, title),
|
|
10
|
+
branding && React.createElement(Branding, { src: branding.src, srcSet: branding.srcSet, alt: branding.alt })),
|
|
11
|
+
price && React.createElement(Price, null, price),
|
|
12
|
+
preamble && React.createElement(Preamble, null, preamble),
|
|
13
|
+
readMoreCta && React.createElement(ReadMoreBtn, { onClick: readMoreCta }, "L\u00E4s mer"))));
|
|
14
|
+
export default AccessoryCard;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
import { size } from '../../layout/helpers';
|
|
3
|
+
import { ButtonReset } from '../Button/index';
|
|
4
|
+
export const Wrapper = styled.div `
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: ${size(2)};
|
|
8
|
+
height: 100%;
|
|
9
|
+
`;
|
|
10
|
+
const imageBaseStyling = css `
|
|
11
|
+
flex-shrink: 0;
|
|
12
|
+
aspect-ratio: 16 / 9;
|
|
13
|
+
border-radius: 3px;
|
|
14
|
+
`;
|
|
15
|
+
export const Image = styled.img `
|
|
16
|
+
${imageBaseStyling};
|
|
17
|
+
display: block;
|
|
18
|
+
width: 100%;
|
|
19
|
+
object-fit: cover;
|
|
20
|
+
`;
|
|
21
|
+
export const ImageaPlaceholder = styled.div `
|
|
22
|
+
${imageBaseStyling};
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
color: ${(props) => props.theme.color.textDarkLighten};
|
|
27
|
+
font-size: 2rem;
|
|
28
|
+
background-color: ${(props) => props.theme.color.accent};
|
|
29
|
+
`;
|
|
30
|
+
export const Body = styled.div `
|
|
31
|
+
flex: 1 1 auto;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: flex-start;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: ${size(1)};
|
|
36
|
+
`;
|
|
37
|
+
export const Heading = styled.div `
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
gap: ${size(2)};
|
|
42
|
+
`;
|
|
43
|
+
export const Title = styled.div.attrs(() => ({
|
|
44
|
+
className: 'wayke__theme wayke__font--bold',
|
|
45
|
+
})) `
|
|
46
|
+
font-size: 1.125rem;
|
|
47
|
+
`;
|
|
48
|
+
export const Branding = styled.img `
|
|
49
|
+
display: block;
|
|
50
|
+
`;
|
|
51
|
+
export const Price = styled.div.attrs(() => ({
|
|
52
|
+
className: 'wayke__theme wayke__font--bold',
|
|
53
|
+
})) ``;
|
|
54
|
+
export const Preamble = styled.div `
|
|
55
|
+
--preamble-max-height: ${size(6)};
|
|
56
|
+
--preamble-gradient-height: calc(var(--preamble-max-height) / 2);
|
|
57
|
+
|
|
58
|
+
position: relative;
|
|
59
|
+
max-height: var(--preamble-max-height);
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
display: -webkit-box;
|
|
62
|
+
-webkit-box-orient: vertical;
|
|
63
|
+
-webkit-line-clamp: 2;
|
|
64
|
+
white-space: pre-wrap;
|
|
65
|
+
|
|
66
|
+
&::before {
|
|
67
|
+
content: '';
|
|
68
|
+
position: absolute;
|
|
69
|
+
top: var(--preamble-max-height);
|
|
70
|
+
left: 0;
|
|
71
|
+
width: 100%;
|
|
72
|
+
height: var(--preamble-gradient-height);
|
|
73
|
+
transform: translateY(-100%); // Only visible if Preamble reaches max height
|
|
74
|
+
color: ${(props) => props.theme.color.bg};
|
|
75
|
+
mask-image: ${(props) => props.theme.mask.gradient.toTop};
|
|
76
|
+
pointer-events: none;
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
export const ReadMoreBtn = styled(ButtonReset).attrs(() => ({
|
|
80
|
+
className: 'wayke__theme wayke__font--bold wayke__color--primary-text',
|
|
81
|
+
})) `
|
|
82
|
+
margin-top: auto;
|
|
83
|
+
display: block;
|
|
84
|
+
color: ${(props) => props.theme.color.link};
|
|
85
|
+
font-size: 0.875rem;
|
|
86
|
+
|
|
87
|
+
&:hover {
|
|
88
|
+
text-decoration: underline;
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
@@ -4,6 +4,7 @@ import scrollIntoView from 'scroll-into-view-if-needed';
|
|
|
4
4
|
import { Wrapper, SliderWrapper, Item, Image, ArrowLeft, ArrowRight, Main, Alt, QuickNav, QuickNavItem, QuickNavBtn, QuickNavImg, EnableNavigationButton, } from './wrapper';
|
|
5
5
|
import { IconChevronLeft, IconChevronRight } from '../Icon/index';
|
|
6
6
|
import { ButtonSecondary, ButtonContent } from '../Button/index';
|
|
7
|
+
import { NavButton } from '../NavButton/index';
|
|
7
8
|
import Lightbox from '../Lightbox/index';
|
|
8
9
|
import EmbededVideo from '../Video/EmbededVideo';
|
|
9
10
|
import QuickNavEmbeded from '../Video/QuickNavEmbeded';
|
|
@@ -97,10 +98,12 @@ const Gallery = ({ media, placeholderImage }) => {
|
|
|
97
98
|
React.createElement(ButtonSecondary, { onClick: onEnableNavigation, title: "St\u00E4ng" },
|
|
98
99
|
React.createElement(ButtonContent, null, "St\u00E4ng"))))),
|
|
99
100
|
!navigationDisabled && mediaLength > 1 && (React.createElement(React.Fragment, null,
|
|
100
|
-
React.createElement(ArrowLeft,
|
|
101
|
-
React.createElement(
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
React.createElement(ArrowLeft, null,
|
|
102
|
+
React.createElement(NavButton, { onClick: prevImage, title: "F\u00F6reg\u00E5ende bild" },
|
|
103
|
+
React.createElement(IconChevronLeft, { block: true }))),
|
|
104
|
+
React.createElement(ArrowRight, null,
|
|
105
|
+
React.createElement(NavButton, { onClick: nextImage, title: "N\u00E4sta bild" },
|
|
106
|
+
React.createElement(IconChevronRight, { block: true })))))),
|
|
104
107
|
React.createElement(Alt, null,
|
|
105
108
|
React.createElement(QuickNav, { ref: quickNavRef }, !!media?.length ? (media.map((m, i) => {
|
|
106
109
|
const src = m.files[0].formats.filter(notEmpty).find((x) => x.format === '225x150')?.url ||
|
|
@@ -149,47 +149,10 @@ export const ImageFull = styled.img `
|
|
|
149
149
|
object-fit: cover;
|
|
150
150
|
`;
|
|
151
151
|
// Arrow Nav
|
|
152
|
-
const Arrow = styled
|
|
152
|
+
const Arrow = styled.div `
|
|
153
153
|
position: absolute;
|
|
154
154
|
top: 50%;
|
|
155
155
|
transform: translateY(-50%);
|
|
156
|
-
display: flex;
|
|
157
|
-
align-items: center;
|
|
158
|
-
justify-content: center;
|
|
159
|
-
width: ${size(4)};
|
|
160
|
-
height: ${size(4)};
|
|
161
|
-
border-radius: 50%;
|
|
162
|
-
font-size: 1rem;
|
|
163
|
-
line-height: 1;
|
|
164
|
-
background-color: rgba(255, 255, 255, 0.4);
|
|
165
|
-
backdrop-filter: blur(5px);
|
|
166
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
167
|
-
color: ${(props) => props.theme.color.textDark};
|
|
168
|
-
transform-origin: 50% 50%;
|
|
169
|
-
transition: transform 200ms ease;
|
|
170
|
-
|
|
171
|
-
:before {
|
|
172
|
-
content: '';
|
|
173
|
-
position: absolute;
|
|
174
|
-
top: 0;
|
|
175
|
-
bottom: 0;
|
|
176
|
-
left: 0;
|
|
177
|
-
right: 0;
|
|
178
|
-
border-radius: 50%;
|
|
179
|
-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
180
|
-
opacity: 0;
|
|
181
|
-
transition: opacity 200ms ease;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
:hover {
|
|
185
|
-
:before {
|
|
186
|
-
opacity: 1;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
:active {
|
|
191
|
-
transform: translateY(-50%) scale(0.95);
|
|
192
|
-
}
|
|
193
156
|
|
|
194
157
|
${(props) => props.theme.breakpoint.LtSm} {
|
|
195
158
|
display: none;
|
|
@@ -34,3 +34,5 @@ export const IconExclamationCircle = ({ block }) => (React.createElement(Icon, {
|
|
|
34
34
|
React.createElement("path", { d: "M14.482 21.893c0-0.857 0.694-1.551 1.551-1.551s1.551 0.694 1.551 1.551c0 0.857-0.694 1.551-1.551 1.551s-1.551-0.694-1.551-1.551zM16.033 2.838c-7.271 0-13.187 5.916-13.187 13.187s5.916 13.187 13.187 13.187 13.187-5.916 13.187-13.187c0-7.271-5.916-13.187-13.187-13.187zM16.033 27.203c-6.163 0-11.178-5.015-11.178-11.178s5.015-11.178 11.178-11.178c6.164 0 11.178 5.015 11.178 11.178s-5.014 11.178-11.178 11.178zM16.033 8.308c-0.555 0-1.004 0.45-1.004 1.004v8.227c0 0.555 0.45 1.004 1.004 1.004s1.004-0.45 1.004-1.004v-8.227c0-0.555-0.45-1.004-1.004-1.004z" })));
|
|
35
35
|
export const IconExclamationTriangle = ({ block }) => (React.createElement(Icon, { viewBox: "0 0 32 32", block: block },
|
|
36
36
|
React.createElement("path", { d: "M15.951 19.663c-0.457 0-0.828-0.37-0.828-0.828v-4.357c0-0.457 0.37-0.828 0.828-0.828s0.828 0.37 0.828 0.828v4.357c0 0.457-0.371 0.828-0.828 0.828zM15.952 22.906c-0.305 0-0.606-0.126-0.821-0.341s-0.338-0.514-0.338-0.818 0.122-0.606 0.338-0.821c0.431-0.43 1.209-0.43 1.639 0 0.215 0.215 0.341 0.516 0.341 0.821s-0.126 0.603-0.341 0.818c-0.215 0.215-0.513 0.341-0.818 0.341zM27.049 27.070h-22.196c-0.352 0-0.677-0.186-0.855-0.489s-0.184-0.677-0.014-0.985l11.098-20.105c0.175-0.317 0.508-0.513 0.87-0.513s0.695 0.197 0.87 0.513l11.098 20.105c0.17 0.308 0.165 0.683-0.014 0.985s-0.504 0.489-0.856 0.489zM6.536 25.083h18.83l-9.415-17.056-9.415 17.056z" })));
|
|
37
|
+
export const IconImage = ({ block }) => (React.createElement(Icon, { viewBox: "0 0 16 16", block: block },
|
|
38
|
+
React.createElement("path", { d: "M14.4 1.6H1.6C.7 1.6 0 2.3 0 3.2v9.6c0 .9.7 1.6 1.6 1.6h12.8c.9 0 1.6-.7 1.6-1.6V3.2c0-.9-.7-1.6-1.6-1.6zm0 11.2H1.6V3.2h12.8v9.6zM3.2 6c0-.7.5-1.2 1.2-1.2s1.2.5 1.2 1.2-.5 1.2-1.2 1.2S3.2 6.7 3.2 6zm.2 4.5l6.2-4.4c.4-.3.9-.2 1.1.2v.1l2.4 4.2c.1.2 0 .4-.1.5-.1 0-.1.1-.2.1H3.7c-.2 0-.4-.2-.4-.4 0-.1 0-.3.1-.3z", fillRule: "evenodd", clipRule: "evenodd" })));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
export const Link = styled.a.attrs(() => ({
|
|
3
|
+
className: 'wayke__theme wayke__color--primary-text',
|
|
4
|
+
})) `
|
|
5
|
+
text-decoration: none;
|
|
6
|
+
cursor: pointer;
|
|
7
|
+
text-decoration: underline;
|
|
8
|
+
|
|
9
|
+
:not(:hover) {
|
|
10
|
+
color: ${(props) => props.theme.color.textDark};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&:hover {
|
|
14
|
+
text-decoration: underline;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
${({ $hoverGhost }) => $hoverGhost &&
|
|
18
|
+
css `
|
|
19
|
+
&:before {
|
|
20
|
+
content: '';
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 0;
|
|
23
|
+
left: 0;
|
|
24
|
+
width: 100%;
|
|
25
|
+
height: 100%;
|
|
26
|
+
}
|
|
27
|
+
`}
|
|
28
|
+
`;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { size } from '../../layout/helpers';
|
|
3
|
+
import { ButtonReset } from '../Button/index';
|
|
4
|
+
export const NavButton = styled(ButtonReset) `
|
|
5
|
+
position: relative;
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
width: ${size(4)};
|
|
10
|
+
height: ${size(4)};
|
|
11
|
+
border-radius: 50%;
|
|
12
|
+
background-color: ${(props) => props.theme.color.bg};
|
|
13
|
+
color: ${(props) => props.theme.color.textDark};
|
|
14
|
+
font-size: 0.75rem;
|
|
15
|
+
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
|
|
16
|
+
transition: transform 200ms ease;
|
|
17
|
+
|
|
18
|
+
&::before {
|
|
19
|
+
content: '';
|
|
20
|
+
position: absolute;
|
|
21
|
+
top: 0;
|
|
22
|
+
bottom: 0;
|
|
23
|
+
left: 0;
|
|
24
|
+
right: 0;
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
|
27
|
+
opacity: 0;
|
|
28
|
+
transition: opacity 200ms ease;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&:enabled {
|
|
32
|
+
&:hover {
|
|
33
|
+
&::before {
|
|
34
|
+
opacity: 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
&:active {
|
|
40
|
+
transform: scale(0.95);
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
@@ -1,66 +1,56 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
${(props) => props.theme.breakpoint.Sm} {
|
|
58
|
-
width: calc(50% - ${size(3)});
|
|
59
|
-
max-width: calc(50% - ${size(3)});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
${(props) => props.theme.breakpoint.Md} {
|
|
63
|
-
width: 33.333%;
|
|
64
|
-
max-width: 33.333%;
|
|
65
|
-
}
|
|
66
|
-
`;
|
|
1
|
+
import React, { useRef, useCallback, useState, useEffect } from 'react';
|
|
2
|
+
import { Wrapper, ListWrapper, List, Item, PrevBtn, NextBtn } from './wrapper';
|
|
3
|
+
import { IconChevronLeft, IconChevronRight } from '../Icon/index';
|
|
4
|
+
import { NavButton } from '../NavButton/index';
|
|
5
|
+
const OverflowGrid = ({ items, itemKeyBase, spacing, columns, columnsSm, columnsMd, accentBg, }) => {
|
|
6
|
+
const overflowElement = useRef(null);
|
|
7
|
+
const onPrev = useCallback(() => {
|
|
8
|
+
const itemWidth = overflowElement.current?.children[0].clientWidth || 0;
|
|
9
|
+
const scrollLeft = overflowElement.current?.scrollLeft || 0;
|
|
10
|
+
if (scrollLeft > 0) {
|
|
11
|
+
overflowElement.current?.scroll({
|
|
12
|
+
left: scrollLeft - itemWidth,
|
|
13
|
+
behavior: 'smooth',
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
}, []);
|
|
17
|
+
const onNext = useCallback(() => {
|
|
18
|
+
const itemWidth = overflowElement.current?.children[0].clientWidth || 0;
|
|
19
|
+
const scrollWidth = overflowElement.current?.scrollWidth || 0;
|
|
20
|
+
const scrollLeft = overflowElement.current?.scrollLeft || 0;
|
|
21
|
+
const overflowElementWidth = overflowElement.current?.offsetWidth || 0;
|
|
22
|
+
const overflowElementScrollWidth = scrollWidth - overflowElementWidth;
|
|
23
|
+
if (overflowElementScrollWidth !== scrollLeft) {
|
|
24
|
+
overflowElement.current?.scroll({
|
|
25
|
+
left: scrollLeft + itemWidth,
|
|
26
|
+
behavior: 'smooth',
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
// Hide navigation buttons depending on scroll position
|
|
31
|
+
const [hidePrevNav, setHidePrevNav] = useState(true);
|
|
32
|
+
const [hideNextNav, setHideNextNav] = useState(true);
|
|
33
|
+
const onScroll = () => {
|
|
34
|
+
const scrollLeft = overflowElement.current?.scrollLeft || 0;
|
|
35
|
+
const scrollWidth = overflowElement.current?.scrollWidth || 0;
|
|
36
|
+
const overflowElementWidth = overflowElement.current?.offsetWidth || 0;
|
|
37
|
+
const overflowElementScrollWidth = scrollWidth - overflowElementWidth;
|
|
38
|
+
setHidePrevNav(scrollLeft == 0 ? true : false);
|
|
39
|
+
setHideNextNav(overflowElementScrollWidth == scrollLeft ? true : false);
|
|
40
|
+
};
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
onScroll();
|
|
43
|
+
overflowElement.current?.addEventListener('scroll', onScroll);
|
|
44
|
+
return () => overflowElement.current?.removeEventListener('scroll', onScroll);
|
|
45
|
+
}, []);
|
|
46
|
+
return (React.createElement(Wrapper, { "$accentBg": accentBg },
|
|
47
|
+
React.createElement(ListWrapper, { "$spacing": spacing },
|
|
48
|
+
React.createElement(List, { ref: overflowElement, "$columns": columns, "$columnsSm": columnsSm, "$columnsMd": columnsMd }, items.map((item, i) => (React.createElement(Item, { key: itemKeyBase ? `${itemKeyBase}-${i}` : `overflow-grid-item-${i}` }, item))))),
|
|
49
|
+
!hidePrevNav && (React.createElement(PrevBtn, null,
|
|
50
|
+
React.createElement(NavButton, { onClick: onPrev, title: "Previous" },
|
|
51
|
+
React.createElement(IconChevronLeft, null)))),
|
|
52
|
+
!hideNextNav && (React.createElement(NextBtn, null,
|
|
53
|
+
React.createElement(NavButton, { onClick: onNext, title: "Next" },
|
|
54
|
+
React.createElement(IconChevronRight, null))))));
|
|
55
|
+
};
|
|
56
|
+
export default OverflowGrid;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
import { size } from '../../layout/helpers';
|
|
3
|
+
export const Wrapper = styled.div `
|
|
4
|
+
--container-gutter: ${size(2)};
|
|
5
|
+
--bg: ${(props) => props.theme.color.bg};
|
|
6
|
+
|
|
7
|
+
${(props) => props.theme.breakpoint.Sm} {
|
|
8
|
+
--container-gutter: ${size(3)};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
position: relative;
|
|
12
|
+
|
|
13
|
+
${({ $accentBg }) => $accentBg &&
|
|
14
|
+
css `
|
|
15
|
+
--bg: ${(props) => props.theme.color.accent};
|
|
16
|
+
`}
|
|
17
|
+
`;
|
|
18
|
+
export const Item = styled.li `
|
|
19
|
+
flex: 1 0 auto;
|
|
20
|
+
width: 100%;
|
|
21
|
+
max-width: 100%;
|
|
22
|
+
scroll-snap-align: start;
|
|
23
|
+
|
|
24
|
+
${(props) => props.theme.breakpoint.LtSm} {
|
|
25
|
+
&:is(:only-child, #important) {
|
|
26
|
+
width: 100%;
|
|
27
|
+
max-width: 100%;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
export const List = styled.ul `
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: stretch;
|
|
34
|
+
list-style: none;
|
|
35
|
+
padding: 0 var(--container-gutter);
|
|
36
|
+
margin: 0;
|
|
37
|
+
overflow: auto;
|
|
38
|
+
scroll-snap-type: x mandatory;
|
|
39
|
+
scroll-padding-left: var(--container-gutter);
|
|
40
|
+
scrollbar-width: none;
|
|
41
|
+
-ms-overflow-style: none;
|
|
42
|
+
|
|
43
|
+
&::-webkit-scrollbar {
|
|
44
|
+
display: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
${({ $columns }) => $columns &&
|
|
48
|
+
css `
|
|
49
|
+
${Item} {
|
|
50
|
+
width: ${100 / $columns}%;
|
|
51
|
+
max-width: ${100 / $columns}%;
|
|
52
|
+
}
|
|
53
|
+
`}
|
|
54
|
+
|
|
55
|
+
${({ $columnsSm }) => $columnsSm &&
|
|
56
|
+
css `
|
|
57
|
+
${(props) => props.theme.breakpoint.Sm} {
|
|
58
|
+
${Item} {
|
|
59
|
+
width: ${100 / $columnsSm}%;
|
|
60
|
+
max-width: ${100 / $columnsSm}%;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
`}
|
|
64
|
+
|
|
65
|
+
${({ $columnsMd }) => $columnsMd &&
|
|
66
|
+
css `
|
|
67
|
+
${(props) => props.theme.breakpoint.Md} {
|
|
68
|
+
${Item} {
|
|
69
|
+
width: ${100 / $columnsMd}%;
|
|
70
|
+
max-width: ${100 / $columnsMd}%;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
`}
|
|
74
|
+
`;
|
|
75
|
+
export const ListWrapper = styled.div `
|
|
76
|
+
position: relative;
|
|
77
|
+
overflow: hidden;
|
|
78
|
+
margin: 0 calc(var(--container-gutter) * -1);
|
|
79
|
+
|
|
80
|
+
${(props) => props.theme.breakpoint.Md} {
|
|
81
|
+
&::before,
|
|
82
|
+
&::after {
|
|
83
|
+
content: '';
|
|
84
|
+
position: absolute;
|
|
85
|
+
top: 0;
|
|
86
|
+
bottom: 0;
|
|
87
|
+
width: var(--container-gutter);
|
|
88
|
+
background-color: var(--bg);
|
|
89
|
+
z-index: 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&::before {
|
|
93
|
+
left: 0;
|
|
94
|
+
|
|
95
|
+
${({ $spacing }) => $spacing
|
|
96
|
+
? css `
|
|
97
|
+
mask-image: linear-gradient(to right, #000 ${size($spacing)}, transparent);
|
|
98
|
+
`
|
|
99
|
+
: css `
|
|
100
|
+
mask-image: ${(props) => props.theme.mask.gradient.toRight};
|
|
101
|
+
`}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
&::after {
|
|
105
|
+
right: 0;
|
|
106
|
+
|
|
107
|
+
${({ $spacing }) => $spacing
|
|
108
|
+
? css `
|
|
109
|
+
mask-image: linear-gradient(to left, #000 ${size($spacing)}, transparent);
|
|
110
|
+
`
|
|
111
|
+
: css `
|
|
112
|
+
mask-image: ${(props) => props.theme.mask.gradient.toLeft};
|
|
113
|
+
`}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
${({ $spacing }) => $spacing &&
|
|
118
|
+
css `
|
|
119
|
+
${List} {
|
|
120
|
+
margin: 0 -${size($spacing / 2)};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
${Item} {
|
|
124
|
+
padding: 0 ${size($spacing / 2)};
|
|
125
|
+
}
|
|
126
|
+
`}
|
|
127
|
+
`;
|
|
128
|
+
const NavBtn = styled.div `
|
|
129
|
+
position: absolute;
|
|
130
|
+
top: 50%;
|
|
131
|
+
z-index: 2;
|
|
132
|
+
|
|
133
|
+
${(props) => props.theme.breakpoint.LtSm} {
|
|
134
|
+
display: none;
|
|
135
|
+
}
|
|
136
|
+
`;
|
|
137
|
+
export const PrevBtn = styled(NavBtn) `
|
|
138
|
+
left: 0;
|
|
139
|
+
transform: translate(-50%, -50%);
|
|
140
|
+
`;
|
|
141
|
+
export const NextBtn = styled(NavBtn) `
|
|
142
|
+
right: 0;
|
|
143
|
+
transform: translate(50%, -50%);
|
|
144
|
+
`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Wrapper, Row, Label, Price } from './wrapper';
|
|
3
|
+
const PriceBox = ({ prices }) => (React.createElement(Wrapper, null, prices?.map((item, i) => (React.createElement(Row, { key: `${item.label}-${i}`, "$oldPrice": item.oldPrice },
|
|
4
|
+
React.createElement(Label, null, item.label),
|
|
5
|
+
React.createElement(Price, null, item.price))))));
|
|
6
|
+
export default PriceBox;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
import { size } from '../../layout/helpers';
|
|
3
|
+
export const Wrapper = styled.ul `
|
|
4
|
+
--price-box-gutter: ${size(2)};
|
|
5
|
+
|
|
6
|
+
list-style: none;
|
|
7
|
+
padding: var(--price-box-gutter);
|
|
8
|
+
margin: 0;
|
|
9
|
+
border: 1px solid ${(props) => props.theme.color.border};
|
|
10
|
+
`;
|
|
11
|
+
export const Row = styled.li `
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: center;
|
|
14
|
+
justify-content: space-between;
|
|
15
|
+
gap: var(--price-box-gutter);
|
|
16
|
+
|
|
17
|
+
& + & {
|
|
18
|
+
margin-top: calc(var(--price-box-gutter) / 4);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
${({ $oldPrice }) => $oldPrice &&
|
|
22
|
+
css `
|
|
23
|
+
--price-text-decoration: line-through;
|
|
24
|
+
|
|
25
|
+
color: ${(props) => props.theme.color.textDarkLighten};
|
|
26
|
+
font-size: 0.875rem;
|
|
27
|
+
`}
|
|
28
|
+
`;
|
|
29
|
+
export const Label = styled.div ``;
|
|
30
|
+
export const Price = styled.div.attrs(() => ({
|
|
31
|
+
className: 'wayke__theme wayke__font--bold',
|
|
32
|
+
})) `
|
|
33
|
+
text-decoration: var(--price-text-decoration);
|
|
34
|
+
`;
|
package/dist-src/layout/theme.js
CHANGED
|
@@ -50,6 +50,14 @@ const theme = {
|
|
|
50
50
|
Lg: '@media (min-width: 1200px)',
|
|
51
51
|
LtLg: '@media (max-width: 1199px)',
|
|
52
52
|
},
|
|
53
|
+
mask: {
|
|
54
|
+
gradient: {
|
|
55
|
+
toTop: 'linear-gradient(to top, #000, transparent)',
|
|
56
|
+
toBottom: 'linear-gradient(to bottom, #000, transparent)',
|
|
57
|
+
toLeft: 'linear-gradient(to left, #000, transparent)',
|
|
58
|
+
toRight: 'linear-gradient(to right, #000, transparent)',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
53
61
|
distances: {
|
|
54
62
|
baseUnit: '8px',
|
|
55
63
|
inputHeight: '48px',
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { marked } from 'marked';
|
|
3
|
+
import { numberSeparator } from '../../../utils/formats';
|
|
4
|
+
import { Repeat } from '../../../components/Repeat/index';
|
|
5
|
+
import { Image } from '../../../components/Image/index';
|
|
6
|
+
import { Link } from '../../../components/Link/index';
|
|
7
|
+
import { UtilityTextBold } from '../../../components/Utility/index';
|
|
8
|
+
import Modal from '../../../components/Modal/index';
|
|
9
|
+
import PriceBox from '../../../components/PriceBox/index';
|
|
10
|
+
import LogoBox from '../../../components/LogoBox/index';
|
|
11
|
+
import Content from '../../../components/Content/index';
|
|
12
|
+
const AccesoryModal = ({ accessory, onClose }) => {
|
|
13
|
+
const priceList = [
|
|
14
|
+
{
|
|
15
|
+
label: 'Kontantpris',
|
|
16
|
+
price: `${numberSeparator(accessory.price)} kr`,
|
|
17
|
+
oldPrice: false,
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
if (accessory.salePrice) {
|
|
21
|
+
priceList.push({
|
|
22
|
+
label: 'Tidigare pris',
|
|
23
|
+
price: `${numberSeparator(accessory.salePrice)} kr`,
|
|
24
|
+
oldPrice: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (accessory.assemblyPrice) {
|
|
28
|
+
priceList.push({
|
|
29
|
+
label: 'Monteringskostnad',
|
|
30
|
+
price: `${numberSeparator(accessory.assemblyPrice)} kr`,
|
|
31
|
+
oldPrice: false,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return (React.createElement(Modal, { title: accessory.name, onClose: onClose },
|
|
35
|
+
accessory.images?.[0] && (React.createElement(Repeat, null,
|
|
36
|
+
React.createElement(Image, { src: `${accessory.images[0]}?w=632`, alt: `${accessory.name}` }))),
|
|
37
|
+
accessory.logotype && (React.createElement(Repeat, null,
|
|
38
|
+
React.createElement(LogoBox, { logo: `${accessory.logotype}?w=160`, logo2x: `${accessory.logotype}?w=320`, alt: accessory.manufacturer || 'Logotyp', wide: true }))),
|
|
39
|
+
React.createElement(Repeat, null,
|
|
40
|
+
React.createElement(PriceBox, { prices: priceList })),
|
|
41
|
+
React.createElement(Repeat, null,
|
|
42
|
+
React.createElement(Content, { dangerouslySetInnerHTML: { __html: marked(accessory.description) } })),
|
|
43
|
+
accessory.articleNumber && (React.createElement(Repeat, null,
|
|
44
|
+
React.createElement(UtilityTextBold, { as: "div" }, "Art. nr."),
|
|
45
|
+
React.createElement("div", null, accessory.articleNumber))),
|
|
46
|
+
accessory.productPage && accessory.productPage.url && (React.createElement(Repeat, null,
|
|
47
|
+
React.createElement(Link, { href: accessory.productPage.url, title: `Mer information om ${accessory.name}`, target: "_blank", rel: "noopener noreferrer nofollow" },
|
|
48
|
+
accessory.productPage.title || 'Läs mer',
|
|
49
|
+
" (\u00F6ppnas i ny flik)")))));
|
|
50
|
+
};
|
|
51
|
+
export default AccesoryModal;
|