@wayke-se/components-react 3.5.3-beta.1 → 3.6.0-beta.1

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 (40) hide show
  1. package/dist-node/index.js +622 -127
  2. package/dist-node/index.js.map +1 -1
  3. package/dist-src/components/AccessoryCard/index.js +14 -0
  4. package/dist-src/components/AccessoryCard/wrapper.js +90 -0
  5. package/dist-src/components/DocumentList/wrapper.js +1 -1
  6. package/dist-src/components/Gallery/index.js +7 -4
  7. package/dist-src/components/Gallery/wrapper.js +1 -38
  8. package/dist-src/components/Icon/index.js +2 -0
  9. package/dist-src/components/Image/index.js +5 -0
  10. package/dist-src/components/Link/index.js +28 -0
  11. package/dist-src/components/NavButton/index.js +42 -0
  12. package/dist-src/components/OverflowGrid/index.js +56 -66
  13. package/dist-src/components/OverflowGrid/wrapper.js +144 -0
  14. package/dist-src/components/PriceBox/index.js +6 -0
  15. package/dist-src/components/PriceBox/wrapper.js +34 -0
  16. package/dist-src/layout/theme.js +8 -0
  17. package/dist-src/layouts/searchItem/Accessories/AccessoryModal.js +51 -0
  18. package/dist-src/layouts/searchItem/Accessories/index.js +32 -0
  19. package/dist-src/layouts/searchItem/Related.js +23 -25
  20. package/dist-src/layouts/searchItem/index.js +3 -1
  21. package/dist-src/queries/SEARCH_ITEM_BRANCH_QUERY.js +18 -0
  22. package/dist-src/queries/SEARCH_ITEM_QUERY.js +18 -0
  23. package/dist-types/@types/codegen/types.d.ts +17 -0
  24. package/dist-types/components/AccessoryCard/index.d.ts +16 -0
  25. package/dist-types/components/AccessoryCard/wrapper.d.ts +18 -0
  26. package/dist-types/components/Gallery/wrapper.d.ts +2 -6
  27. package/dist-types/components/Icon/index.d.ts +1 -0
  28. package/dist-types/components/Image/index.d.ts +1 -0
  29. package/dist-types/components/Link/index.d.ts +7 -0
  30. package/dist-types/components/NavButton/index.d.ts +3 -0
  31. package/dist-types/components/OverflowGrid/index.d.ts +12 -3
  32. package/dist-types/components/OverflowGrid/wrapper.d.ts +18 -0
  33. package/dist-types/components/PriceBox/index.d.ts +11 -0
  34. package/dist-types/components/PriceBox/wrapper.d.ts +10 -0
  35. package/dist-types/layout/theme.d.ts +8 -0
  36. package/dist-types/layouts/searchItem/Accessories/AccessoryModal.d.ts +8 -0
  37. package/dist-types/layouts/searchItem/Accessories/index.d.ts +7 -0
  38. package/dist-web/index.js +535 -122
  39. package/dist-web/index.js.map +1 -1
  40. 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
+ `;
@@ -38,7 +38,7 @@ export const PdfSubtitle = styled.div `
38
38
  color: ${(props) => props.theme.color.textDarkLighten};
39
39
  `;
40
40
  export const PdfColumn = styled.div `
41
- flex 0 0 auto;
41
+ flex: 0 0 auto;
42
42
  &:first-child {
43
43
  flex: 1 1 auto;
44
44
  }
@@ -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, { onClick: prevImage, title: "F\u00F6reg\u00E5ende bild" },
101
- React.createElement(IconChevronLeft, { block: true })),
102
- React.createElement(ArrowRight, { onClick: nextImage, title: "N\u00E4sta bild" },
103
- React.createElement(IconChevronRight, { block: true }))))),
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 ||
@@ -218,47 +218,10 @@ export const ImageFull = styled.img `
218
218
  object-fit: cover;
219
219
  `;
220
220
  // Arrow Nav
221
- const Arrow = styled(ButtonReset) `
221
+ const Arrow = styled.div `
222
222
  position: absolute;
223
223
  top: 50%;
224
224
  transform: translateY(-50%);
225
- display: flex;
226
- align-items: center;
227
- justify-content: center;
228
- width: ${size(4)};
229
- height: ${size(4)};
230
- border-radius: 50%;
231
- font-size: 1rem;
232
- line-height: 1;
233
- background-color: rgba(255, 255, 255, 0.4);
234
- backdrop-filter: blur(5px);
235
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
236
- color: ${(props) => props.theme.color.textDark};
237
- transform-origin: 50% 50%;
238
- transition: transform 200ms ease;
239
-
240
- :before {
241
- content: '';
242
- position: absolute;
243
- top: 0;
244
- bottom: 0;
245
- left: 0;
246
- right: 0;
247
- border-radius: 50%;
248
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
249
- opacity: 0;
250
- transition: opacity 200ms ease;
251
- }
252
-
253
- :hover {
254
- :before {
255
- opacity: 1;
256
- }
257
- }
258
-
259
- :active {
260
- transform: translateY(-50%) scale(0.95);
261
- }
262
225
 
263
226
  ${(props) => props.theme.breakpoint.LtSm} {
264
227
  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,5 @@
1
+ import styled from 'styled-components';
2
+ export const Image = styled.img `
3
+ display: block;
4
+ width: 100%;
5
+ `;
@@ -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 styled from 'styled-components';
2
- import { size } from '../../layout/helpers';
3
- import { Wrapper as Container } from '../Container/wrapper';
4
- export const OverflowGrid = styled.div `
5
- ${(props) => props.theme.breakpoint.LtMd} {
6
- ${Container} & {
7
- margin-left: ${size(-3)};
8
- margin-right: ${size(-2)};
9
- }
10
- }
11
-
12
- ${(props) => props.theme.breakpoint.LtSm} {
13
- ${Container} & {
14
- margin-left: ${size(-2)};
15
- margin-right: ${size(-1)};
16
- }
17
- }
18
- `;
19
- export const OverflowGridList = styled.ul `
20
- display: flex;
21
- align-items: flex-start;
22
- list-style: none;
23
- padding: 0;
24
- margin: ${size(-1)};
25
-
26
- ${(props) => props.theme.breakpoint.LtMd} {
27
- ${Container} & {
28
- overflow: auto;
29
- -webkit-overflow-scrolling: touch;
30
- padding: 0 ${size(3)};
31
-
32
- :after {
33
- content: '';
34
- flex: 0 0 auto;
35
- height: 1px;
36
- width: ${size(2)};
37
- }
38
- }
39
- }
40
-
41
- ${(props) => props.theme.breakpoint.LtSm} {
42
- ${Container} & {
43
- padding: 0 ${size(2)};
44
-
45
- :after {
46
- width: ${size(1)};
47
- }
48
- }
49
- }
50
- `;
51
- export const OverflowGridItem = styled.li `
52
- flex: 1 0 auto;
53
- width: calc(100% - ${size(2)});
54
- max-width: calc(100% - ${size(2)});
55
- padding: ${size(1)};
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
+ `;
@@ -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;