@dnanpm/styleguide 3.12.1 → 3.12.3

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 (33) hide show
  1. package/build/cjs/assets/fonts/fonts.css.js +3 -0
  2. package/build/cjs/components/Breadcrumb/Breadcrumb.d.ts +42 -0
  3. package/build/cjs/components/Breadcrumb/Breadcrumb.js +90 -0
  4. package/build/cjs/components/ButtonCard/ButtonCard.js +3 -5
  5. package/build/cjs/components/Carousel/Carousel.d.ts +11 -14
  6. package/build/cjs/components/Carousel/Carousel.js +52 -40
  7. package/build/cjs/components/Hero/Hero.d.ts +0 -6
  8. package/build/cjs/components/Hero/Hero.js +3 -3
  9. package/build/cjs/components/index.d.ts +1 -0
  10. package/build/cjs/index.js +2 -0
  11. package/build/cjs/themes/globalStyles.js +1 -0
  12. package/build/cjs/themes/theme.d.ts +9 -2
  13. package/build/cjs/themes/themeComponents/breakpoints.d.ts +9 -4
  14. package/build/cjs/utils/styledUtils.d.ts +22 -1
  15. package/build/cjs/utils/styledUtils.js +26 -6
  16. package/build/es/assets/fonts/fonts.css.js +1 -0
  17. package/build/es/components/Breadcrumb/Breadcrumb.d.ts +42 -0
  18. package/build/es/components/Breadcrumb/Breadcrumb.js +82 -0
  19. package/build/es/components/ButtonCard/ButtonCard.js +3 -5
  20. package/build/es/components/Carousel/Carousel.d.ts +11 -14
  21. package/build/es/components/Carousel/Carousel.js +52 -40
  22. package/build/es/components/Hero/Hero.d.ts +0 -6
  23. package/build/es/components/Hero/Hero.js +3 -3
  24. package/build/es/components/index.d.ts +1 -0
  25. package/build/es/index.js +1 -0
  26. package/build/es/themes/globalStyles.js +1 -0
  27. package/build/es/themes/theme.d.ts +9 -2
  28. package/build/es/themes/themeComponents/breakpoints.d.ts +9 -4
  29. package/build/es/utils/styledUtils.d.ts +22 -1
  30. package/build/es/utils/styledUtils.js +26 -6
  31. package/package.json +12 -8
  32. package/build/cjs/build/assets/fonts/fonts.css +0 -129
  33. package/build/es/build/assets/fonts/fonts.css +0 -129
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ if(typeof document!=="undefined")document.head.appendChild(document.createElement("style")).textContent="@font-face{font-family:'DNA Text';font-style: normal;font-weight: 400;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Regular.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Regular.woff') format('woff');}@font-face{font-family:'DNA Text';font-style: normal;font-weight: 500;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Medium.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Medium.woff') format('woff');}@font-face{font-family:'DNA Text';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading';font-style: normal;font-weight: 600;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff') format('woff');}@font-face{font-family:'DNA Heading';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading';font-style: normal;font-weight: 900;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Black.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Black.woff') format('woff');}@font-face{font-family:'DNA Numerals';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNANumerals-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNANumerals-Bold.woff') format('woff');}@font-face{font-family:'DNA Text Regular';font-style: normal;font-weight: 400;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Regular.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Regular.woff') format('woff');}@font-face{font-family:'DNA Text Medium';font-style: normal;font-weight: 500;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Medium.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Medium.woff') format('woff');}@font-face{font-family:'DNA Text Bold';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading Demi Bold';font-style: normal;font-weight: 600;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff') format('woff');}@font-face{font-family:'DNA Heading Bold';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading Black';font-style: normal;font-weight: 900;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Black.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Black.woff') format('woff');}@font-face{font-family:'DNA Numerals Bold';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNANumerals-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNANumerals-Bold.woff') format('woff');}";
@@ -0,0 +1,42 @@
1
+ import type { ComponentType } from 'react';
2
+ import React from 'react';
3
+ export interface BreadcrumbItem {
4
+ /**
5
+ * Display text for the breadcrumb item
6
+ */
7
+ label: string;
8
+ /**
9
+ * URL/path for the breadcrumb item. If not provided, item will be rendered as text only
10
+ */
11
+ href?: string;
12
+ }
13
+ interface Props {
14
+ /**
15
+ * Array of breadcrumb items to display
16
+ */
17
+ items?: BreadcrumbItem[];
18
+ /**
19
+ * Custom link component to use instead of default anchor element
20
+ * Useful for router integration (e.g., Next.js Link, React Router Link)
21
+ */
22
+ linkComponent?: ComponentType<any>;
23
+ /**
24
+ * Props to pass to the link component
25
+ */
26
+ linkProps?: Record<string, unknown>;
27
+ /**
28
+ * Screen reader label describing the breadcrumb navigation
29
+ */
30
+ ariaLabel?: string;
31
+ /**
32
+ * Allows to pass testid string for testing purposes
33
+ */
34
+ 'data-testid'?: string;
35
+ /**
36
+ * Allows to pass a custom className
37
+ */
38
+ className?: string;
39
+ }
40
+ declare const Breadcrumb: ({ "data-testid": dataTestId, ariaLabel, className, items, linkComponent: LinkComponent, linkProps, }: Props) => React.JSX.Element | null;
41
+ /** @component */
42
+ export default Breadcrumb;
@@ -0,0 +1,90 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var icons = require('@dnanpm/icons');
7
+ var styledComponents = require('styled-components');
8
+ var theme = require('../../themes/theme.js');
9
+ var styledUtils = require('../../utils/styledUtils.js');
10
+
11
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
12
+
13
+ var React__default = /*#__PURE__*/_interopDefaultCompat(React);
14
+
15
+ const BreadcrumbNav = styledComponents.styled.nav `
16
+ font-size: ${theme.default.fontSize.s};
17
+ font-weight: ${theme.default.fontWeight.medium};
18
+ `;
19
+ const BreadcrumbList = styledComponents.styled.ol `
20
+ display: flex;
21
+ align-items: center;
22
+ flex-wrap: nowrap;
23
+ list-style: none;
24
+ margin: ${styledUtils.getMultipliedSize(theme.default.base.baseHeight, 2)} 0;
25
+ padding: 0;
26
+ gap: ${styledUtils.getMultipliedSize(theme.default.base.baseHeight, 0.5)};
27
+ overflow: visible;
28
+ container: breadcrumb / inline-size;
29
+
30
+ /* Responsive behavior: show only last 2 items when container < 600px */
31
+ @container (max-width: 599px) {
32
+ li:not(:nth-last-child(-n + 2)) {
33
+ display: none;
34
+ }
35
+ }
36
+ `;
37
+ const BreadcrumbListItem = styledComponents.styled.li `
38
+ display: flex;
39
+ align-items: center;
40
+ gap: ${styledUtils.getMultipliedSize(theme.default.base.baseHeight, 0.5)};
41
+
42
+ &:last-child {
43
+ min-width: 0;
44
+ }
45
+
46
+ a {
47
+ &:focus-visible {
48
+ outline: none;
49
+ border-radius: ${theme.default.radius.s};
50
+ box-shadow:
51
+ 0px 0px 0px 2px ${theme.default.color.focus.light},
52
+ 0px 0px 0px 4px ${theme.default.color.focus.dark};
53
+ }
54
+ }
55
+
56
+ span {
57
+ flex: 1 1 0%;
58
+ white-space: nowrap;
59
+ overflow: hidden;
60
+ text-overflow: ellipsis;
61
+ }
62
+ `;
63
+ const Breadcrumb = ({ 'data-testid': dataTestId, ariaLabel, className, items, linkComponent: LinkComponent, linkProps = {}, }) => {
64
+ if (!items || items.length === 0) {
65
+ return null;
66
+ }
67
+ const renderItem = (item, index) => {
68
+ const isLastItem = index === items.length - 1;
69
+ if (isLastItem || !item.href) {
70
+ return React__default.default.createElement("span", { "aria-current": isLastItem ? 'page' : undefined }, item.label);
71
+ }
72
+ if (LinkComponent) {
73
+ return (React__default.default.createElement(LinkComponent, Object.assign({ href: item.href, itemProp: "item", itemScope: true, itemType: "https://schema.org/WebPage" }, linkProps),
74
+ React__default.default.createElement("span", { itemProp: "name" }, item.label)));
75
+ }
76
+ return (React__default.default.createElement("a", { href: item.href, itemProp: "item", itemScope: true, itemType: "https://schema.org/WebPage" },
77
+ React__default.default.createElement("span", { itemProp: "name" }, item.label)));
78
+ };
79
+ return (React__default.default.createElement(BreadcrumbNav, { "aria-label": ariaLabel, className: className, "data-testid": dataTestId },
80
+ React__default.default.createElement(BreadcrumbList, { itemScope: true, itemType: "https://schema.org/BreadcrumbList" }, items.map((item, index) => {
81
+ var _a;
82
+ const isLastItem = index === items.length - 1;
83
+ return (React__default.default.createElement(BreadcrumbListItem, { itemProp: "itemListElement", itemScope: true, itemType: "https://schema.org/ListItem", key: `breadcrumb-${item.label}-${(_a = item.href) !== null && _a !== void 0 ? _a : 'nolink'}` },
84
+ renderItem(item, index),
85
+ React__default.default.createElement("meta", { itemProp: "position", content: (index + 1).toString() }),
86
+ !isLastItem && (React__default.default.createElement(icons.ChevronRight, { color: theme.default.color.background.pink.default, size: "0.9rem" }))));
87
+ }))));
88
+ };
89
+
90
+ exports.default = Breadcrumb;
@@ -17,7 +17,7 @@ var React__default = /*#__PURE__*/_interopDefaultCompat(React);
17
17
  const ButtonCardWrapper = styledComponents.styled.button `
18
18
  display: block;
19
19
  padding: 0;
20
- border: 0 none;
20
+ border: 0;
21
21
  width: 100%;
22
22
  background-color: transparent;
23
23
  cursor: pointer;
@@ -25,6 +25,7 @@ const ButtonCardWrapper = styledComponents.styled.button `
25
25
  font-size: ${theme.default.fontSize.default};
26
26
  line-height: ${theme.default.lineHeight.default};
27
27
  color: ${theme.default.color.text.black};
28
+ text-decoration: none;
28
29
 
29
30
  &:focus-visible {
30
31
  border: 1px solid ${theme.default.color.focus.light};
@@ -33,13 +34,12 @@ const ButtonCardWrapper = styledComponents.styled.button `
33
34
  outline: none;
34
35
 
35
36
  & > div {
36
- border: 0 none;
37
+ border: 0;
37
38
  }
38
39
  }
39
40
 
40
41
  &:hover {
41
42
  color: ${theme.default.color.text.black};
42
- text-decoration: none;
43
43
  }
44
44
  `;
45
45
  const StyledBox = styledComponents.styled(Box.default) `
@@ -74,7 +74,6 @@ const Title = styledComponents.styled.div `
74
74
  font-size: ${theme.default.fontSize.default};
75
75
  line-height: ${theme.default.lineHeight.default};
76
76
  font-weight: ${theme.default.fontWeight.bold};
77
-
78
77
  text-overflow: ellipsis;
79
78
  overflow: hidden;
80
79
  white-space: nowrap;
@@ -97,7 +96,6 @@ const Subtitle = styledComponents.styled.div `
97
96
  font-size: ${theme.default.fontSize.s};
98
97
  line-height: ${theme.default.lineHeight.s};
99
98
  font-weight: ${theme.default.fontWeight.medium};
100
-
101
99
  text-overflow: ellipsis;
102
100
  overflow: hidden;
103
101
  white-space: nowrap;
@@ -1,11 +1,5 @@
1
1
  import type { MouseEvent, ReactNode } from 'react';
2
2
  import React from 'react';
3
- interface Responsive {
4
- minItems: number;
5
- maxItems: number;
6
- minWidth: number;
7
- maxWidth: number;
8
- }
9
3
  interface Props {
10
4
  /**
11
5
  * Unique ID for the component
@@ -50,10 +44,12 @@ interface Props {
50
44
  */
51
45
  className?: string;
52
46
  /**
53
- * Allows to define responsive configuration
54
- * If not provided, visibleItems property will be used
47
+ * Allows for responsive behavior in the carousel.
48
+ * Shows as many items as possible; each item requires a defined width.
49
+ * This overrides the `visibleItems` prop.
50
+ * @default false
55
51
  */
56
- responsive?: Partial<Responsive>;
52
+ responsive?: boolean;
57
53
  /**
58
54
  * Allows to pass a screen reader label for the pagination item next to the current slide number
59
55
  */
@@ -73,12 +69,13 @@ interface Props {
73
69
  */
74
70
  swipeStep?: number;
75
71
  }
76
- declare const SlideItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, Required<{
77
- $visibleItems?: Props["visibleItems"];
78
- }> & {
79
- $itemWidthCorrection: number;
72
+ declare const SlideItem: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components/dist/types").Substitute<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {
80
73
  $isSwiping: boolean;
81
- }>> & string;
74
+ $responsive: boolean;
75
+ $gap: number;
76
+ } & Partial<Required<{
77
+ $visibleItems: Props["visibleItems"];
78
+ }>>>> & string;
82
79
  /** @visibleName Carousel */
83
80
  declare const Carousel: ({ "data-testid": dataTestId, ...props }: Props) => React.JSX.Element;
84
81
  /** @component */
@@ -41,7 +41,6 @@ const SlidesWrapper = styledComponents.styled.div `
41
41
  display: flex;
42
42
  width: 100%;
43
43
  height: 100%;
44
- gap: ${({ $gap }) => $gap}rem;
45
44
  transition-property: transform;
46
45
  transform: translate3d(0px, 0, 0);
47
46
  transition-duration: 0ms;
@@ -52,9 +51,14 @@ const SlideItem = styledComponents.styled.div `
52
51
  display: block;
53
52
  position: relative;
54
53
  flex-shrink: 0;
55
- flex-basis: calc(
56
- ${({ $visibleItems, $itemWidthCorrection }) => `(100% / ${$visibleItems}) - ${$itemWidthCorrection}px`}
57
- );
54
+ padding-right: ${({ $gap }) => $gap}rem;
55
+
56
+ ${({ $responsive, $visibleItems }) => !$responsive && $visibleItems
57
+ ? `flex-basis: calc(100% / ${$visibleItems});`
58
+ : `
59
+ flex-basis: auto;
60
+ width: max-content;
61
+ `}
58
62
 
59
63
  a {
60
64
  pointer-events: ${({ $isSwiping }) => ($isSwiping ? 'none' : 'auto')};
@@ -146,53 +150,51 @@ const Counter = styledComponents.styled.span `
146
150
  `;
147
151
  /** @visibleName Carousel */
148
152
  const Carousel = (_a) => {
149
- var _b;
153
+ var _b, _c;
150
154
  var { 'data-testid': dataTestId } = _a, props = tslib.__rest(_a, ['data-testid']);
151
155
  const slidesWrapperRef = React.useRef(null);
152
156
  const scrollbarRef = React.useRef(null);
153
157
  const knobRef = React.useRef(null);
154
- const { isMobile, width } = useWindowSize.default(theme.default.breakpoints.md);
158
+ const firstItemRef = React.useRef(null);
159
+ const { isMobile, width: windowWidth } = useWindowSize.default(theme.default.breakpoints.md);
155
160
  const [currentIndex, setCurrentIndex] = React.useState(0);
156
161
  const [isSwiping, setIsSwiping] = React.useState(false);
157
- const [calculatedItems, setCalculatedItems] = React.useState(props.visibleItems || (isMobile ? 1.2 : 1));
162
+ const [autoVisibleItems, setAutoVisibleItems] = React.useState(null);
158
163
  React.useEffect(() => {
159
- const calculateVisibleItems = () => {
160
- const defaultValue = props.visibleItems || (isMobile ? 1.2 : 1);
161
- const { minItems, maxItems, minWidth, maxWidth } = props.responsive || {};
162
- if (!width || !minItems || !maxItems || !minWidth || !maxWidth) {
163
- return defaultValue;
164
- }
165
- const calculatedMaxItems = React.Children.count(props.children) === 1 ? 1 : maxItems;
166
- if (width < minWidth) {
167
- return minItems;
164
+ if (props.responsive) {
165
+ const container = slidesWrapperRef.current;
166
+ const firstItem = firstItemRef.current;
167
+ if (container && firstItem) {
168
+ Array.from(container.children).forEach(itemElement => {
169
+ const item = itemElement;
170
+ item.style.flexBasis = '';
171
+ item.style.width = '';
172
+ });
173
+ const containerWidth = container.offsetWidth;
174
+ const itemWidth = firstItem.offsetWidth;
175
+ if (itemWidth > 0) {
176
+ const realVisibleItems = containerWidth / itemWidth;
177
+ setAutoVisibleItems(Math.max(1, realVisibleItems));
178
+ }
168
179
  }
169
- if (width > maxWidth) {
170
- return calculatedMaxItems;
171
- }
172
- return minItems + ((width - minWidth) / (maxWidth - minWidth)) * (maxItems - minItems);
173
- };
174
- const timeoutId = setTimeout(() => {
175
- setCalculatedItems(calculateVisibleItems());
176
- }, 100);
177
- return () => clearTimeout(timeoutId);
178
- }, [width, isMobile, props.responsive, props.visibleItems, props.children]);
180
+ }
181
+ else {
182
+ setAutoVisibleItems(null);
183
+ }
184
+ }, [props.responsive, windowWidth, props.children]);
179
185
  const getStep = (step, visibleItems) => {
180
186
  if (step > visibleItems) {
181
187
  return Math.floor(visibleItems);
182
188
  }
183
189
  return Math.floor(step);
184
190
  };
185
- const visibleItems = props.visibleItems || calculatedItems;
191
+ const visibleItems = (_b = autoVisibleItems !== null && autoVisibleItems !== void 0 ? autoVisibleItems : props.visibleItems) !== null && _b !== void 0 ? _b : (isMobile ? 1.2 : 1);
186
192
  const slidesWrapperGapSizePx = 20;
187
193
  const slidesCount = React.Children.count(props.children);
188
194
  const slideScreensCount = Math.max(1, slidesCount - Math.floor(visibleItems) + 1);
189
- const step = getStep((_b = props.swipeStep) !== null && _b !== void 0 ? _b : 1, visibleItems);
195
+ const step = getStep((_c = props.swipeStep) !== null && _c !== void 0 ? _c : 1, visibleItems);
190
196
  const currentStepIndex = Math.ceil(currentIndex / step);
191
197
  const totalSwipeSteps = Math.ceil(slideScreensCount / step + ((slideScreensCount - 1) % step !== 0 ? 1 : 0));
192
- const itemWidthCorrectionRatio = (slidesWrapperGapSizePx * visibleItems) % Math.floor(visibleItems) === 0
193
- ? (visibleItems - 1) / visibleItems
194
- : Math.floor(visibleItems) / visibleItems;
195
- const itemWidthCorrection = itemWidthCorrectionRatio * slidesWrapperGapSizePx;
196
198
  const data = React.useMemo(() => ({
197
199
  startX: 0,
198
200
  startTime: 0,
@@ -328,12 +330,10 @@ const Carousel = (_a) => {
328
330
  React.useEffect(() => {
329
331
  if (slidesWrapperRef.current && scrollbarRef.current) {
330
332
  const isRest = React.Children.count(props.children) - (currentIndex + visibleItems) < 0;
331
- data.itemWidth =
332
- slidesWrapperRef.current.offsetWidth / visibleItems - itemWidthCorrection;
333
+ data.itemWidth = slidesWrapperRef.current.offsetWidth / visibleItems;
333
334
  data.scrollWidth = slidesWrapperRef.current.scrollWidth;
334
335
  data.lastItemX =
335
- (data.itemWidth + slidesWrapperGapSizePx) *
336
- (React.Children.count(props.children) - visibleItems) -
336
+ data.itemWidth * (React.Children.count(props.children) - visibleItems) -
337
337
  (isRest ? slidesWrapperGapSizePx * (Math.ceil(visibleItems) - visibleItems) : 0);
338
338
  data.scrollbarToSlidesRatio =
339
339
  data.lastItemX /
@@ -342,14 +342,26 @@ const Carousel = (_a) => {
342
342
  let slidesTransform = 0;
343
343
  if (React.Children.count(props.children) >= visibleItems) {
344
344
  slidesTransform =
345
- data.itemWidth * currentIndex +
346
- slidesWrapperGapSizePx * currentIndex -
345
+ data.itemWidth * currentIndex -
347
346
  (isRest ? data.itemWidth * (visibleItems % 1) + slidesWrapperGapSizePx : 0);
348
347
  }
349
348
  setElementTransform(slidesWrapperRef, -slidesTransform);
350
349
  setElementTransform(knobRef, slidesTransform / data.scrollbarToSlidesRatio);
351
350
  }
352
- }, [currentIndex, data, itemWidthCorrection, props.children, slideScreensCount, visibleItems]);
351
+ }, [currentIndex, data, props.children, slideScreensCount, visibleItems]);
352
+ React.useEffect(() => {
353
+ var _a;
354
+ if (props.responsive && autoVisibleItems) {
355
+ const items = (_a = slidesWrapperRef.current) === null || _a === void 0 ? void 0 : _a.children;
356
+ if (items) {
357
+ Array.from(items).forEach(itemElement => {
358
+ const item = itemElement;
359
+ item.style.flexBasis = `calc(100% / ${autoVisibleItems})`;
360
+ item.style.width = '';
361
+ });
362
+ }
363
+ }
364
+ }, [autoVisibleItems, props.responsive]);
353
365
  return (React__default.default.createElement(CarouselWrapper, { id: props.id, className: props.className, "data-testid": dataTestId },
354
366
  React__default.default.createElement(Header, { "data-testid": dataTestId && `${dataTestId}-header` },
355
367
  props.title && React__default.default.createElement(Title, null, props.title),
@@ -361,7 +373,7 @@ const Carousel = (_a) => {
361
373
  React__default.default.createElement(ButtonArrow.default, { direction: "left", "aria-label": props.previousAriaLabel, onClick: handleNavigationButtonPreviousClick, disabled: currentIndex <= 0, type: "button" }),
362
374
  React__default.default.createElement(ButtonArrow.default, { direction: "right", "aria-label": props.nextAriaLabel, onClick: handleNavigationButtonNextClick, disabled: currentIndex + visibleItems >= React.Children.count(props.children), type: "button" }))),
363
375
  React__default.default.createElement(Content, { "data-testid": dataTestId && `${dataTestId}-content` },
364
- React__default.default.createElement(SlidesWrapper, { ref: slidesWrapperRef, onPointerDown: handleSlidesPointerDown, "$gap": slidesWrapperGapSizePx / 16 }, React.Children.map(props.children, child => (React__default.default.createElement(SlideItem, { "$visibleItems": visibleItems, "$itemWidthCorrection": itemWidthCorrection, "$isSwiping": isSwiping, onPointerDown: handlePointerDown }, child))))),
376
+ React__default.default.createElement(SlidesWrapper, { ref: slidesWrapperRef, onPointerDown: handleSlidesPointerDown }, React.Children.map(props.children, (child, index) => (React__default.default.createElement(SlideItem, { ref: index === 0 ? firstItemRef : undefined, "$visibleItems": visibleItems, "$isSwiping": isSwiping, onPointerDown: handlePointerDown, "$responsive": Boolean(props.responsive), "$gap": slidesWrapperGapSizePx / 16 }, child))))),
365
377
  React__default.default.createElement(Footer, { "data-testid": dataTestId && `${dataTestId}-footer` },
366
378
  React__default.default.createElement(Pagination, null, [...Array(totalSwipeSteps).keys()].map((value, index) => (React__default.default.createElement(PaginationItem, { key: value, "aria-label": props.paginationAriaLabel &&
367
379
  `${props.paginationAriaLabel} ${index + 1}`, "aria-current": Math.ceil(currentIndex / step) === index, "$isActive": Math.ceil(currentIndex / step) === index, onClick: handlePaginationItemClick, type: "button" })))),
@@ -34,12 +34,6 @@ interface HeroProps {
34
34
  * Background color when no image is provided
35
35
  */
36
36
  backgroundColor?: string;
37
- /**
38
- * Enable gradient overlay on background
39
- *
40
- * @default false
41
- */
42
- hasGradient?: boolean;
43
37
  /**
44
38
  * Logo image component for logo-style heroes
45
39
  */
@@ -35,9 +35,9 @@ const HeroImage = styledComponents.styled.div `
35
35
  height: ${HERO_CONSTANTS.mobileHeight}px;
36
36
  background-color: ${({ $backgroundColor }) => $backgroundColor || 'transparent'};
37
37
 
38
- ${({ $hasGradient }) => $hasGradient &&
38
+ ${({ $backgroundColor }) => $backgroundColor &&
39
39
  `
40
- linear-gradient(180deg, ${theme.default.color.background.plum.default}${theme.default.color.transparency.T0} 0%, ${theme.default.color.background.plum.default}${theme.default.color.transparency.T30} 100%);
40
+ background-image: linear-gradient(180deg, ${theme.default.color.background.plum.default}${theme.default.color.transparency.T0} 0%, ${theme.default.color.background.plum.default}${theme.default.color.transparency.T30} 100%);
41
41
  background-size: 100% 33.33%;
42
42
  background-repeat: no-repeat;
43
43
  background-position: bottom;
@@ -184,7 +184,7 @@ const Hero = (_a) => {
184
184
  var { variant = 'default', headingLevel = 'h1', Image = 'img', LogoImage = 'img', 'data-testid': dataTestId } = _a, props = tslib.__rest(_a, ["variant", "headingLevel", "Image", "LogoImage", 'data-testid']);
185
185
  const HeadingTag = headingLevel;
186
186
  return (React__default.default.createElement(HeroContainer, { "$variant": variant, className: props.className, "data-testid": dataTestId },
187
- React__default.default.createElement(HeroImage, { "$hasGradient": props.hasGradient, "$backgroundColor": props.backgroundColor },
187
+ React__default.default.createElement(HeroImage, { "$backgroundColor": props.backgroundColor },
188
188
  props.logoImageProps && (React__default.default.createElement(LogoImageWrap, null,
189
189
  React__default.default.createElement(LogoImageContainer, null, renderImage(LogoImage, props.logoImageProps)))),
190
190
  !props.logoImageProps && props.imageProps && renderImage(Image, props.imageProps)),
@@ -2,6 +2,7 @@ export { default as Accordion } from './Accordion/Accordion';
2
2
  export { default as AccordionItem } from './AccordionItem/AccordionItem';
3
3
  export { default as AmountSelector } from './AmountSelector/AmountSelector';
4
4
  export { default as Box } from './Box/Box';
5
+ export { default as Breadcrumb } from './Breadcrumb/Breadcrumb';
5
6
  export { default as Button } from './Button/Button';
6
7
  export { default as ButtonArrow } from './ButtonArrow/ButtonArrow';
7
8
  export { default as ButtonCard } from './ButtonCard/ButtonCard';
@@ -4,6 +4,7 @@ var Accordion = require('./components/Accordion/Accordion.js');
4
4
  var AccordionItem = require('./components/AccordionItem/AccordionItem.js');
5
5
  var AmountSelector = require('./components/AmountSelector/AmountSelector.js');
6
6
  var Box = require('./components/Box/Box.js');
7
+ var Breadcrumb = require('./components/Breadcrumb/Breadcrumb.js');
7
8
  var Button = require('./components/Button/Button.js');
8
9
  var ButtonArrow = require('./components/ButtonArrow/ButtonArrow.js');
9
10
  var ButtonCard = require('./components/ButtonCard/ButtonCard.js');
@@ -176,6 +177,7 @@ exports.Accordion = Accordion.default;
176
177
  exports.AccordionItem = AccordionItem.default;
177
178
  exports.AmountSelector = AmountSelector.default;
178
179
  exports.Box = Box.default;
180
+ exports.Breadcrumb = Breadcrumb.default;
179
181
  exports.Button = Button.default;
180
182
  exports.ButtonArrow = ButtonArrow.default;
181
183
  exports.ButtonCard = ButtonCard.default;
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
 
3
+ require('../assets/fonts/fonts.css.js');
3
4
  var styledUtils = require('../utils/styledUtils.js');
4
5
  var styledComponents = require('styled-components');
5
6
  var theme = require('./theme.js');
@@ -9,7 +9,14 @@ declare const theme: {
9
9
  unit: string;
10
10
  };
11
11
  };
12
- breakpoints: import("./themeComponents/breakpoints").ViewBreakpoints;
12
+ breakpoints: {
13
+ readonly xxl: 1440;
14
+ readonly xl: 1200;
15
+ readonly lg: 992;
16
+ readonly md: 768;
17
+ readonly sm: 576;
18
+ readonly xs: 480;
19
+ };
13
20
  color: {
14
21
  default: {
15
22
  pink: string;
@@ -155,7 +162,7 @@ declare const theme: {
155
162
  h1: string;
156
163
  h2: string;
157
164
  };
158
- media: Record<string | number, (l: TemplateStringsArray, ...p: (string | number)[]) => string>;
165
+ media: Record<"xxl" | "xl" | "lg" | "md" | "sm" | "xs", (l: TemplateStringsArray, ...p: (string | number)[]) => ReturnType<typeof import("styled-components").css>>;
159
166
  radius: {
160
167
  default: string;
161
168
  s: string;
@@ -1,5 +1,10 @@
1
- export interface ViewBreakpoints {
2
- [key: string]: number;
3
- }
4
- declare const breakpoints: ViewBreakpoints;
1
+ declare const breakpoints: {
2
+ readonly xxl: 1440;
3
+ readonly xl: 1200;
4
+ readonly lg: 992;
5
+ readonly md: 768;
6
+ readonly sm: 576;
7
+ readonly xs: 480;
8
+ };
9
+ export type ViewBreakpoints = typeof breakpoints;
5
10
  export default breakpoints;
@@ -1,3 +1,4 @@
1
+ import { css } from '../themes/styled';
1
2
  export declare const getMultipliedSize: (base: {
2
3
  value: number;
3
4
  unit: string;
@@ -6,4 +7,24 @@ export declare const getDividedSize: (base: {
6
7
  value: number;
7
8
  unit: string;
8
9
  }, divide: number) => string;
9
- export declare const media: Record<string | number, (l: TemplateStringsArray, ...p: (string | number)[]) => string>;
10
+ /**
11
+ * Media query helpers for responsive design.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const StyledDiv = styled.div`
16
+ * font-size: 1rem;
17
+ * ${media.md`font-size: 1.2rem;`}
18
+ * ${media.lg`font-size: 1.5rem;`}
19
+ * `;
20
+ * ```
21
+ *
22
+ * Available breakpoints:
23
+ * - `xs`: 480px
24
+ * - `sm`: 576px
25
+ * - `md`: 768px
26
+ * - `lg`: 992px
27
+ * - `xl`: 1200px
28
+ * - `xxl`: 1440px
29
+ */
30
+ export declare const media: Record<"xxl" | "xl" | "lg" | "md" | "sm" | "xs", (l: TemplateStringsArray, ...p: (string | number)[]) => ReturnType<typeof css>>;
@@ -5,12 +5,32 @@ var breakpoints = require('../themes/themeComponents/breakpoints.js');
5
5
 
6
6
  const getMultipliedSize = (base, multiply) => `${multiply * base.value}${base.unit}`;
7
7
  const getDividedSize = (base, divide) => `${base.value / divide}${base.unit}`;
8
- const media = Object.keys(breakpoints.default).reduce((acc, label) => {
9
- acc[label] = (literals, ...placeholders) => styledComponents.css `
10
- @media (min-width: ${breakpoints.default[label]}px) {
11
- ${styledComponents.css(literals, ...placeholders)};
12
- }
13
- `.join('');
8
+ /**
9
+ * Media query helpers for responsive design.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const StyledDiv = styled.div`
14
+ * font-size: 1rem;
15
+ * ${media.md`font-size: 1.2rem;`}
16
+ * ${media.lg`font-size: 1.5rem;`}
17
+ * `;
18
+ * ```
19
+ *
20
+ * Available breakpoints:
21
+ * - `xs`: 480px
22
+ * - `sm`: 576px
23
+ * - `md`: 768px
24
+ * - `lg`: 992px
25
+ * - `xl`: 1200px
26
+ * - `xxl`: 1440px
27
+ */
28
+ const media = Object.keys(breakpoints.default).reduce((acc, key) => {
29
+ acc[key] = (literals, ...placeholders) => styledComponents.css `
30
+ @media (min-width: ${breakpoints.default[key]}px) {
31
+ ${styledComponents.css(literals, ...placeholders)}
32
+ }
33
+ `;
14
34
  return acc;
15
35
  }, {});
16
36
 
@@ -0,0 +1 @@
1
+ if(typeof document!=="undefined")document.head.appendChild(document.createElement("style")).textContent="@font-face{font-family:'DNA Text';font-style: normal;font-weight: 400;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Regular.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Regular.woff') format('woff');}@font-face{font-family:'DNA Text';font-style: normal;font-weight: 500;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Medium.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Medium.woff') format('woff');}@font-face{font-family:'DNA Text';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading';font-style: normal;font-weight: 600;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff') format('woff');}@font-face{font-family:'DNA Heading';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading';font-style: normal;font-weight: 900;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Black.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Black.woff') format('woff');}@font-face{font-family:'DNA Numerals';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNANumerals-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNANumerals-Bold.woff') format('woff');}@font-face{font-family:'DNA Text Regular';font-style: normal;font-weight: 400;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Regular.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Regular.woff') format('woff');}@font-face{font-family:'DNA Text Medium';font-style: normal;font-weight: 500;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Medium.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Medium.woff') format('woff');}@font-face{font-family:'DNA Text Bold';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAText-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAText-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading Demi Bold';font-style: normal;font-weight: 600;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-DemiBold.woff') format('woff');}@font-face{font-family:'DNA Heading Bold';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Bold.woff') format('woff');}@font-face{font-family:'DNA Heading Black';font-style: normal;font-weight: 900;font-display: swap;src: url('https://www.dna.fi/fonts/DNAHeading-Black.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNAHeading-Black.woff') format('woff');}@font-face{font-family:'DNA Numerals Bold';font-style: normal;font-weight: 700;font-display: swap;src: url('https://www.dna.fi/fonts/DNANumerals-Bold.woff2') format('woff2'),url('https://www.dna.fi/fonts/DNANumerals-Bold.woff') format('woff');}";
@@ -0,0 +1,42 @@
1
+ import type { ComponentType } from 'react';
2
+ import React from 'react';
3
+ export interface BreadcrumbItem {
4
+ /**
5
+ * Display text for the breadcrumb item
6
+ */
7
+ label: string;
8
+ /**
9
+ * URL/path for the breadcrumb item. If not provided, item will be rendered as text only
10
+ */
11
+ href?: string;
12
+ }
13
+ interface Props {
14
+ /**
15
+ * Array of breadcrumb items to display
16
+ */
17
+ items?: BreadcrumbItem[];
18
+ /**
19
+ * Custom link component to use instead of default anchor element
20
+ * Useful for router integration (e.g., Next.js Link, React Router Link)
21
+ */
22
+ linkComponent?: ComponentType<any>;
23
+ /**
24
+ * Props to pass to the link component
25
+ */
26
+ linkProps?: Record<string, unknown>;
27
+ /**
28
+ * Screen reader label describing the breadcrumb navigation
29
+ */
30
+ ariaLabel?: string;
31
+ /**
32
+ * Allows to pass testid string for testing purposes
33
+ */
34
+ 'data-testid'?: string;
35
+ /**
36
+ * Allows to pass a custom className
37
+ */
38
+ className?: string;
39
+ }
40
+ declare const Breadcrumb: ({ "data-testid": dataTestId, ariaLabel, className, items, linkComponent: LinkComponent, linkProps, }: Props) => React.JSX.Element | null;
41
+ /** @component */
42
+ export default Breadcrumb;