@purpurds/tabs 6.12.0 → 6.12.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.
- package/dist/LICENSE.txt +1 -1
- package/dist/styles.css +1 -1
- package/dist/tab-content.d.ts.map +1 -1
- package/dist/tab-header.d.ts.map +1 -1
- package/dist/tabs.cjs.js +3 -3
- package/dist/tabs.cjs.js.map +1 -1
- package/dist/tabs.d.ts +4 -0
- package/dist/tabs.d.ts.map +1 -1
- package/dist/tabs.es.js +390 -371
- package/dist/tabs.es.js.map +1 -1
- package/package.json +8 -6
- package/src/tab-content.tsx +5 -7
- package/src/tab-header.tsx +7 -2
- package/src/tabs.module.scss +20 -3
- package/src/tabs.stories.tsx +46 -3
- package/src/tabs.tsx +38 -7
package/src/tabs.module.scss
CHANGED
|
@@ -23,9 +23,12 @@
|
|
|
23
23
|
color: var(--purpur-color-text-default);
|
|
24
24
|
background: var(--purpur-color-background-primary);
|
|
25
25
|
box-shadow: var(--purpur-shadow-md);
|
|
26
|
-
transition: all var(--purpur-motion-duration-150) var(--purpur-motion-easing-ease-in-out);
|
|
27
26
|
cursor: pointer;
|
|
28
27
|
|
|
28
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
29
|
+
transition: all var(--purpur-motion-duration-150) var(--purpur-motion-easing-ease-in-out);
|
|
30
|
+
}
|
|
31
|
+
|
|
29
32
|
&::after {
|
|
30
33
|
content: "";
|
|
31
34
|
position: absolute;
|
|
@@ -34,7 +37,10 @@
|
|
|
34
37
|
border-radius: var(--purpur-border-radius-xs);
|
|
35
38
|
box-sizing: border-box;
|
|
36
39
|
opacity: 0;
|
|
37
|
-
|
|
40
|
+
|
|
41
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
42
|
+
transition: all var(--purpur-motion-duration-150) var(--purpur-motion-easing-ease-in-out);
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
&:hover {
|
|
@@ -110,7 +116,10 @@
|
|
|
110
116
|
left: 0;
|
|
111
117
|
z-index: 10;
|
|
112
118
|
height: var(--purpur-border-width-sm);
|
|
113
|
-
|
|
119
|
+
|
|
120
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
121
|
+
transition: all var(--purpur-motion-duration-150) var(--purpur-motion-easing-ease-in-out);
|
|
122
|
+
}
|
|
114
123
|
}
|
|
115
124
|
}
|
|
116
125
|
|
|
@@ -134,6 +143,14 @@
|
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
|
|
146
|
+
&__content-wrapper {
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
|
|
149
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
150
|
+
transition: height var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
137
154
|
&--contained,
|
|
138
155
|
&--contained-negative {
|
|
139
156
|
#{$root}__list {
|
package/src/tabs.stories.tsx
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Button } from "@purpurds/button";
|
|
3
|
+
import { Paragraph } from "@purpurds/paragraph";
|
|
3
4
|
import { useArgs } from "@storybook/preview-api";
|
|
4
5
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
5
6
|
|
|
6
7
|
import "@purpurds/button/styles";
|
|
8
|
+
import "@purpurds/icon/styles";
|
|
9
|
+
import "@purpurds/paragraph/styles";
|
|
7
10
|
import { Tabs } from "./tabs";
|
|
8
11
|
import { tabsVariants } from "./tabs.utils";
|
|
9
12
|
|
|
@@ -61,6 +64,11 @@ const meta = {
|
|
|
61
64
|
negative: {
|
|
62
65
|
table: { type: { summary: "boolean" } },
|
|
63
66
|
},
|
|
67
|
+
animateHeight: {
|
|
68
|
+
table: {
|
|
69
|
+
type: { summary: "When true, animates the height of the component when switching tabs." },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
64
72
|
["data-testid"]: { table: { type: { summary: "string" } }, control: { type: "text" } },
|
|
65
73
|
className: {
|
|
66
74
|
table: {
|
|
@@ -89,7 +97,13 @@ export const Showcase: Story = {
|
|
|
89
97
|
name={`${name}-1`}
|
|
90
98
|
style={{ padding: "var(--purpur-spacing-250)" }}
|
|
91
99
|
>
|
|
92
|
-
<div>
|
|
100
|
+
<div>
|
|
101
|
+
<Paragraph>
|
|
102
|
+
Telia is a leading telecom provider in the Nordics and Baltics, offering mobile,
|
|
103
|
+
broadband, and TV services to millions of customers. Known for its reliability and
|
|
104
|
+
coverage, Telia plays a key role in keeping people and businesses connected.
|
|
105
|
+
</Paragraph>
|
|
106
|
+
</div>
|
|
93
107
|
</Tabs.Content>,
|
|
94
108
|
<Tabs.Content
|
|
95
109
|
key="2"
|
|
@@ -97,7 +111,18 @@ export const Showcase: Story = {
|
|
|
97
111
|
name={`${name}-2`}
|
|
98
112
|
style={{ padding: "var(--purpur-spacing-250)" }}
|
|
99
113
|
>
|
|
100
|
-
<div>
|
|
114
|
+
<div>
|
|
115
|
+
<Paragraph style={{ marginBottom: "var(--purpur-spacing-200)" }}>
|
|
116
|
+
Telia is at the forefront of digital innovation in Northern Europe, investing heavily in
|
|
117
|
+
5G networks, fiber broadband, and cloud-based business solutions. Its technology powers
|
|
118
|
+
communication for individuals, families, and companies across the region.
|
|
119
|
+
</Paragraph>
|
|
120
|
+
<Paragraph>
|
|
121
|
+
Beyond connectivity, Telia also offers advanced tools for remote work, IoT, and
|
|
122
|
+
cybersecurity. The company supports both public and private sector partners with
|
|
123
|
+
scalable, secure, and future-ready digital infrastructure.
|
|
124
|
+
</Paragraph>
|
|
125
|
+
</div>
|
|
101
126
|
</Tabs.Content>,
|
|
102
127
|
<Tabs.Content
|
|
103
128
|
key="3"
|
|
@@ -105,10 +130,28 @@ export const Showcase: Story = {
|
|
|
105
130
|
name={`${name}-3`}
|
|
106
131
|
style={{ padding: "var(--purpur-spacing-250)" }}
|
|
107
132
|
>
|
|
108
|
-
<div>
|
|
133
|
+
<div>
|
|
134
|
+
<Paragraph style={{ marginBottom: "var(--purpur-spacing-200)" }}>
|
|
135
|
+
Telia has a long-standing history in telecommunications, with roots stretching back to
|
|
136
|
+
the early days of phone services in Sweden. Today, it operates in multiple countries and
|
|
137
|
+
continues to evolve alongside the needs of a digital society.
|
|
138
|
+
</Paragraph>
|
|
139
|
+
<Paragraph style={{ marginBottom: "var(--purpur-spacing-200)" }}>
|
|
140
|
+
A major focus for Telia is sustainability. The company is working to reduce its
|
|
141
|
+
environmental impact through energy-efficient networks, circular economy initiatives,
|
|
142
|
+
and climate-smart services. Its goal is to become climate-neutral across its entire
|
|
143
|
+
value chain.
|
|
144
|
+
</Paragraph>
|
|
145
|
+
<Paragraph>
|
|
146
|
+
In addition to its consumer offerings, Telia collaborates closely with governments,
|
|
147
|
+
cities, and enterprises. From smart city solutions to secure connectivity for remote
|
|
148
|
+
healthcare, Telia is helping build the digital infrastructure of tomorrow.
|
|
149
|
+
</Paragraph>
|
|
150
|
+
</div>
|
|
109
151
|
</Tabs.Content>,
|
|
110
152
|
],
|
|
111
153
|
negative: false,
|
|
154
|
+
animateHeight: false,
|
|
112
155
|
},
|
|
113
156
|
render: ({ children, ...args }) => (
|
|
114
157
|
<div
|
package/src/tabs.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { Children, ReactElement, useEffect, useRef, useState } from "react";
|
|
1
|
+
import React, { Children, type ReactElement, useEffect, useRef, useState } from "react";
|
|
2
2
|
import { Icon } from "@purpurds/icon";
|
|
3
3
|
import { chevronLeft } from "@purpurds/icon/assets/chevron-left";
|
|
4
4
|
import { chevronRight } from "@purpurds/icon/assets/chevron-right";
|
|
@@ -8,7 +8,12 @@ import c from "classnames/bind";
|
|
|
8
8
|
import { isTabContent, TabContent } from "./tab-content";
|
|
9
9
|
import { TabHeader } from "./tab-header";
|
|
10
10
|
import styles from "./tabs.module.scss";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
createTabChangeDetailEvent,
|
|
13
|
+
type TabChangeDetail,
|
|
14
|
+
type TabsCmp,
|
|
15
|
+
type TabsVariant,
|
|
16
|
+
} from "./tabs.utils";
|
|
12
17
|
|
|
13
18
|
type TabsProps = {
|
|
14
19
|
children: Array<ReactElement<typeof TabContent>> | ReactElement<typeof TabContent>;
|
|
@@ -24,6 +29,10 @@ type TabsProps = {
|
|
|
24
29
|
* */
|
|
25
30
|
onChange?: (event: CustomEvent<TabChangeDetail>) => void;
|
|
26
31
|
defaultValue?: string;
|
|
32
|
+
/**
|
|
33
|
+
* When true, animates the height of the component when switching tabs.
|
|
34
|
+
* */
|
|
35
|
+
animateHeight?: boolean;
|
|
27
36
|
"data-testid"?: string;
|
|
28
37
|
} & Pick<React.HTMLAttributes<HTMLDivElement>, "style" | "className">;
|
|
29
38
|
|
|
@@ -77,10 +86,13 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
77
86
|
onChange,
|
|
78
87
|
className,
|
|
79
88
|
defaultValue,
|
|
89
|
+
animateHeight = false,
|
|
80
90
|
"data-testid": dataTestId,
|
|
81
91
|
value,
|
|
82
92
|
...props
|
|
83
93
|
}) => {
|
|
94
|
+
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
95
|
+
const [height, setHeight] = useState(-1);
|
|
84
96
|
const [scrollClasses, setScrollClasses] = useState<{ [key: string]: boolean }>({});
|
|
85
97
|
const [selectedTriggerOffset, setSelectedTriggerOffset] = useState(0);
|
|
86
98
|
const [selectedTriggerWidth, setSelectedTriggerWidth] = useState(0);
|
|
@@ -160,12 +172,26 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
160
172
|
);
|
|
161
173
|
|
|
162
174
|
useEffect(() => {
|
|
163
|
-
|
|
175
|
+
const setNewHeight = () => {
|
|
176
|
+
if (contentRef.current && animateHeight) {
|
|
177
|
+
const newHeight = contentRef.current.offsetHeight;
|
|
178
|
+
setHeight(newHeight);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const handleResize = () => {
|
|
183
|
+
setNewHeight();
|
|
184
|
+
handleLinePosition();
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
setNewHeight();
|
|
188
|
+
|
|
189
|
+
window.addEventListener("resize", handleResize);
|
|
164
190
|
|
|
165
191
|
return () => {
|
|
166
|
-
window.removeEventListener("resize",
|
|
192
|
+
window.removeEventListener("resize", handleResize);
|
|
167
193
|
};
|
|
168
|
-
}, [activeIndex]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
194
|
+
}, [activeIndex, animateHeight]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
169
195
|
|
|
170
196
|
useEffect(() => {
|
|
171
197
|
handleLinePosition();
|
|
@@ -266,8 +292,13 @@ export const Tabs: TabsCmp<TabsProps> = ({
|
|
|
266
292
|
<ScrollButton side="left" />
|
|
267
293
|
<ScrollButton side="right" />
|
|
268
294
|
</div>
|
|
269
|
-
<div
|
|
270
|
-
{
|
|
295
|
+
<div
|
|
296
|
+
className={cx(`${rootClassName}__content-wrapper`)}
|
|
297
|
+
style={{ height: animateHeight && height > -1 ? `${height}px` : "auto" }}
|
|
298
|
+
>
|
|
299
|
+
<div ref={contentRef} className={cx(`${rootClassName}__content-container`)}>
|
|
300
|
+
{Children.map(tabContentChildren, (child) => child)}
|
|
301
|
+
</div>
|
|
271
302
|
</div>
|
|
272
303
|
</div>
|
|
273
304
|
</Root>
|