@popsure/dirty-swan 0.62.5 → 0.63.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/package.json +1 -1
- package/src/lib/components/comparisonTable/components/AccordionItem/AccordionItem.tsx +42 -5
- package/src/lib/components/comparisonTable/hooks/useComparisonTable.ts +0 -3
- package/src/lib/components/comparisonTable/index.stories.tsx +15 -1
- package/src/lib/components/comparisonTable/index.tsx +21 -3
- package/src/lib/components/dateSelector/components/calendarCaption.module.scss +0 -1
- package/src/lib/components/dateSelector/components/datepicker.scss +0 -1
- package/src/lib/components/table/Table.stories.tsx +57 -1
- package/src/lib/components/table/Table.tsx +9 -0
- package/src/lib/components/table/components/TableContents/Collapsible.tsx +3 -1
- package/src/lib/components/table/components/TableContents/TableContents.tsx +31 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import AnimateHeight from 'react-animate-height';
|
|
2
2
|
|
|
3
3
|
import styles from './AccordionItem.module.scss';
|
|
4
|
-
import { useState } from 'react';
|
|
4
|
+
import { useRef, useState } from 'react';
|
|
5
5
|
|
|
6
6
|
const ChevronSVG = ({ className }: { className?: string }) => (
|
|
7
7
|
<svg
|
|
@@ -27,19 +27,52 @@ export const AccordionItem = ({
|
|
|
27
27
|
className = '',
|
|
28
28
|
headerClassName = '',
|
|
29
29
|
label,
|
|
30
|
+
isOpen: controlledIsOpen,
|
|
31
|
+
onToggle,
|
|
32
|
+
scrollOnOpen,
|
|
33
|
+
scrollTopOffset = 0,
|
|
30
34
|
}: {
|
|
31
35
|
children: React.ReactNode | string;
|
|
32
36
|
className?: string;
|
|
33
37
|
headerClassName?: string;
|
|
34
38
|
label: React.ReactNode;
|
|
39
|
+
isOpen?: boolean;
|
|
40
|
+
onToggle?: () => void;
|
|
41
|
+
scrollOnOpen?: boolean;
|
|
42
|
+
scrollTopOffset?: number;
|
|
35
43
|
}) => {
|
|
36
|
-
const [
|
|
44
|
+
const [internalIsOpen, setInternalIsOpen] = useState(false);
|
|
45
|
+
const sectionRef = useRef<HTMLElement>(null);
|
|
46
|
+
const userToggled = useRef(false);
|
|
47
|
+
|
|
48
|
+
const isControlled = controlledIsOpen !== undefined && onToggle !== undefined;
|
|
49
|
+
const isOpen = isControlled ? controlledIsOpen : internalIsOpen;
|
|
50
|
+
|
|
37
51
|
const handleClick = () => {
|
|
38
|
-
|
|
52
|
+
userToggled.current = true;
|
|
53
|
+
if (isControlled) {
|
|
54
|
+
onToggle();
|
|
55
|
+
} else {
|
|
56
|
+
setInternalIsOpen(!internalIsOpen);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const handleAnimationEnd = () => {
|
|
61
|
+
if (userToggled.current && isOpen && scrollOnOpen && sectionRef.current) {
|
|
62
|
+
userToggled.current = false;
|
|
63
|
+
const top =
|
|
64
|
+
sectionRef.current.getBoundingClientRect().top +
|
|
65
|
+
window.scrollY -
|
|
66
|
+
scrollTopOffset;
|
|
67
|
+
window.scrollTo({ top, behavior: 'smooth' });
|
|
68
|
+
}
|
|
39
69
|
};
|
|
40
70
|
|
|
41
71
|
return (
|
|
42
|
-
<section
|
|
72
|
+
<section
|
|
73
|
+
ref={sectionRef}
|
|
74
|
+
className={`d-flex fd-column ${styles.container} ${className}`}
|
|
75
|
+
>
|
|
43
76
|
<button
|
|
44
77
|
className={`d-flex ai-center jc-between ${styles.headerButton} ${headerClassName}`}
|
|
45
78
|
onClick={handleClick}
|
|
@@ -58,7 +91,11 @@ export const AccordionItem = ({
|
|
|
58
91
|
</button>
|
|
59
92
|
{/* Min height is 0.1 so that the scroll position is correctly synced across accordion items but is not actually shown.
|
|
60
93
|
If set to 0, react-animate-height will set display to "none" which means scrolling is not synced. */}
|
|
61
|
-
<AnimateHeight
|
|
94
|
+
<AnimateHeight
|
|
95
|
+
duration={300}
|
|
96
|
+
height={isOpen ? 'auto' : 0.1}
|
|
97
|
+
onHeightAnimationEnd={handleAnimationEnd}
|
|
98
|
+
>
|
|
62
99
|
{children}
|
|
63
100
|
</AnimateHeight>
|
|
64
101
|
</section>
|
|
@@ -11,7 +11,6 @@ export const useComparisonTable = ({
|
|
|
11
11
|
const [showMore, setShowMore] = useState<boolean>(false);
|
|
12
12
|
const [headerWidth, setHeaderWidth] = useState(1400);
|
|
13
13
|
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
|
|
14
|
-
const [selectedSection, setSelectedSection] = useState('');
|
|
15
14
|
const [headerId, setHeaderId] = useState('');
|
|
16
15
|
const headerRef = useRef<HTMLDivElement | null>(null);
|
|
17
16
|
const contentContainerRef = useRef<HTMLDivElement | null>(null);
|
|
@@ -140,8 +139,6 @@ export const useComparisonTable = ({
|
|
|
140
139
|
headerWidth,
|
|
141
140
|
headerId,
|
|
142
141
|
contentContainerRef,
|
|
143
|
-
selectedSection,
|
|
144
|
-
setSelectedSection,
|
|
145
142
|
selectedTabIndex,
|
|
146
143
|
setSelectedTabIndex,
|
|
147
144
|
headerRefCallbackRef,
|
|
@@ -254,6 +254,14 @@ const story = {
|
|
|
254
254
|
collapsibleSections: {
|
|
255
255
|
description: 'Make table groups with a label collapsible',
|
|
256
256
|
},
|
|
257
|
+
scrollOnOpen: {
|
|
258
|
+
description:
|
|
259
|
+
'When enabled, the page scrolls to the top of a newly expanded section.',
|
|
260
|
+
},
|
|
261
|
+
scrollTopOffset: {
|
|
262
|
+
description:
|
|
263
|
+
'Offset in pixels from the top of the viewport when scrolling to an expanded section.',
|
|
264
|
+
},
|
|
257
265
|
cellWidth: {
|
|
258
266
|
description: 'Width of a table content cell',
|
|
259
267
|
},
|
|
@@ -287,7 +295,9 @@ const story = {
|
|
|
287
295
|
showDetailsCaption: 'Show details',
|
|
288
296
|
hideScrollBars: false,
|
|
289
297
|
hideScrollBarsMobile: true,
|
|
290
|
-
collapsibleSections:
|
|
298
|
+
collapsibleSections: true,
|
|
299
|
+
scrollOnOpen: true,
|
|
300
|
+
scrollTopOffset: 0,
|
|
291
301
|
cellWidth: undefined,
|
|
292
302
|
firstColumnWidth: undefined,
|
|
293
303
|
stickyHeaderTopOffset: 0,
|
|
@@ -325,6 +335,8 @@ export const ComparisonTableStory = {
|
|
|
325
335
|
data,
|
|
326
336
|
headers,
|
|
327
337
|
collapsibleSections,
|
|
338
|
+
scrollOnOpen,
|
|
339
|
+
scrollTopOffset,
|
|
328
340
|
hideDetails,
|
|
329
341
|
classNameOverrides,
|
|
330
342
|
hideDetailsCaption,
|
|
@@ -341,6 +353,8 @@ export const ComparisonTableStory = {
|
|
|
341
353
|
data={data}
|
|
342
354
|
headers={headers}
|
|
343
355
|
collapsibleSections={collapsibleSections}
|
|
356
|
+
scrollOnOpen={scrollOnOpen}
|
|
357
|
+
scrollTopOffset={scrollTopOffset}
|
|
344
358
|
hideDetails={hideDetails}
|
|
345
359
|
classNameOverrides={classNameOverrides}
|
|
346
360
|
hideDetailsCaption={hideDetailsCaption}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { Fragment } from 'react';
|
|
2
|
+
import { Fragment, useRef, useState } from 'react';
|
|
3
3
|
import { ScrollSync, ScrollSyncPane } from 'react-scroll-sync';
|
|
4
4
|
|
|
5
5
|
import { AccordionItem } from './components/AccordionItem';
|
|
@@ -52,6 +52,8 @@ export interface ComparisonTableProps<T> {
|
|
|
52
52
|
hideScrollBars?: boolean;
|
|
53
53
|
hideScrollBarsMobile?: boolean;
|
|
54
54
|
collapsibleSections?: boolean;
|
|
55
|
+
scrollOnOpen?: boolean;
|
|
56
|
+
scrollTopOffset?: number;
|
|
55
57
|
cellWidth?: number;
|
|
56
58
|
firstColumnWidth?: number;
|
|
57
59
|
stickyHeaderTopOffset?: number;
|
|
@@ -83,6 +85,8 @@ const ComparisonTable = <T extends { id: number }>(
|
|
|
83
85
|
hideScrollBars,
|
|
84
86
|
hideScrollBarsMobile = true,
|
|
85
87
|
collapsibleSections,
|
|
88
|
+
scrollOnOpen,
|
|
89
|
+
scrollTopOffset = 0,
|
|
86
90
|
cellWidth,
|
|
87
91
|
firstColumnWidth,
|
|
88
92
|
stickyHeaderTopOffset,
|
|
@@ -94,8 +98,6 @@ const ComparisonTable = <T extends { id: number }>(
|
|
|
94
98
|
headerWidth,
|
|
95
99
|
headerId,
|
|
96
100
|
contentContainerRef,
|
|
97
|
-
selectedSection,
|
|
98
|
-
setSelectedSection,
|
|
99
101
|
selectedTabIndex,
|
|
100
102
|
headerRefCallbackRef,
|
|
101
103
|
handleArrowsClick,
|
|
@@ -103,6 +105,9 @@ const ComparisonTable = <T extends { id: number }>(
|
|
|
103
105
|
showMore,
|
|
104
106
|
} = useComparisonTable({ onSelectionChanged });
|
|
105
107
|
|
|
108
|
+
const [openSectionId, setOpenSectionId] = useState<number | null>(null);
|
|
109
|
+
const stickyHeaderRef = useRef<HTMLDivElement>(null);
|
|
110
|
+
|
|
106
111
|
const cssVariablesStyle = {
|
|
107
112
|
'--tableWidth': `${headerWidth}px`,
|
|
108
113
|
...(cellWidth ? { '--cellWidth': `${cellWidth}px` } : {}),
|
|
@@ -125,6 +130,7 @@ const ComparisonTable = <T extends { id: number }>(
|
|
|
125
130
|
})}
|
|
126
131
|
>
|
|
127
132
|
<div
|
|
133
|
+
ref={stickyHeaderRef}
|
|
128
134
|
id={headerId}
|
|
129
135
|
className={classNames(baseStyles.header, classNameOverrides?.header)}
|
|
130
136
|
>
|
|
@@ -190,6 +196,18 @@ const ComparisonTable = <T extends { id: number }>(
|
|
|
190
196
|
)}
|
|
191
197
|
label={headerGroup.label}
|
|
192
198
|
headerClassName="p24 br8"
|
|
199
|
+
isOpen={openSectionId === headerGroup.id}
|
|
200
|
+
onToggle={() =>
|
|
201
|
+
setOpenSectionId((prev) =>
|
|
202
|
+
prev === headerGroup.id ? null : headerGroup.id
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
scrollOnOpen={scrollOnOpen}
|
|
206
|
+
scrollTopOffset={
|
|
207
|
+
scrollTopOffset +
|
|
208
|
+
(stickyHeaderRef.current?.getBoundingClientRect()
|
|
209
|
+
.height ?? 0)
|
|
210
|
+
}
|
|
193
211
|
>
|
|
194
212
|
<ScrollSyncPane>
|
|
195
213
|
<div
|
|
@@ -143,6 +143,48 @@ const initialData: TableData = [
|
|
|
143
143
|
{ rating: { type: 'zap', value: 3 } },
|
|
144
144
|
{ rating: { type: 'star', value: 3 } },
|
|
145
145
|
],
|
|
146
|
+
[
|
|
147
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
148
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
149
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
150
|
+
{ rating: { type: 'star', value: 3 } },
|
|
151
|
+
],
|
|
152
|
+
[
|
|
153
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
154
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
155
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
156
|
+
{ rating: { type: 'star', value: 3 } },
|
|
157
|
+
],
|
|
158
|
+
[
|
|
159
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
160
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
161
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
162
|
+
{ rating: { type: 'star', value: 3 } },
|
|
163
|
+
],
|
|
164
|
+
[
|
|
165
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
166
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
167
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
168
|
+
{ rating: { type: 'star', value: 3 } },
|
|
169
|
+
],
|
|
170
|
+
[
|
|
171
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
172
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
173
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
174
|
+
{ rating: { type: 'star', value: 3 } },
|
|
175
|
+
],
|
|
176
|
+
[
|
|
177
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
178
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
179
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
180
|
+
{ rating: { type: 'star', value: 3 } },
|
|
181
|
+
],
|
|
182
|
+
[
|
|
183
|
+
{ text: 'Rating', modalContent: 'info' },
|
|
184
|
+
{ rating: { type: 'zap', value: 1 }, modalContent: 'Maybe' },
|
|
185
|
+
{ rating: { type: 'zap', value: 3 } },
|
|
186
|
+
{ rating: { type: 'star', value: 3 } },
|
|
187
|
+
],
|
|
146
188
|
[
|
|
147
189
|
{
|
|
148
190
|
type: 'CARD',
|
|
@@ -203,6 +245,14 @@ const story = {
|
|
|
203
245
|
collapsibleSections: {
|
|
204
246
|
subContent: 'This property allows to collapse the sections of the table.',
|
|
205
247
|
},
|
|
248
|
+
scrollOnOpen: {
|
|
249
|
+
subContent:
|
|
250
|
+
'When enabled, the page scrolls to the top of a newly expanded section.',
|
|
251
|
+
},
|
|
252
|
+
scrollTopOffset: {
|
|
253
|
+
subContent:
|
|
254
|
+
'Offset in pixels from the top of the viewport when scrolling to an expanded section.',
|
|
255
|
+
},
|
|
206
256
|
hideDetails: {
|
|
207
257
|
subContent: 'This property allows to hide the details of the table.',
|
|
208
258
|
},
|
|
@@ -244,7 +294,9 @@ const story = {
|
|
|
244
294
|
},
|
|
245
295
|
args: {
|
|
246
296
|
tableData: initialData,
|
|
247
|
-
collapsibleSections:
|
|
297
|
+
collapsibleSections: true,
|
|
298
|
+
scrollOnOpen: true,
|
|
299
|
+
scrollTopOffset: 0,
|
|
248
300
|
hideDetails: false,
|
|
249
301
|
stickyHeaderTopOffset: 0,
|
|
250
302
|
title: 'Title of the table',
|
|
@@ -262,6 +314,8 @@ const story = {
|
|
|
262
314
|
export const TableStory = {
|
|
263
315
|
render: ({
|
|
264
316
|
collapsibleSections,
|
|
317
|
+
scrollOnOpen,
|
|
318
|
+
scrollTopOffset,
|
|
265
319
|
tableData,
|
|
266
320
|
hideColumns,
|
|
267
321
|
hideDetails,
|
|
@@ -303,6 +357,8 @@ export const TableStory = {
|
|
|
303
357
|
},
|
|
304
358
|
}}
|
|
305
359
|
collapsibleSections={collapsibleSections}
|
|
360
|
+
scrollOnOpen={scrollOnOpen}
|
|
361
|
+
scrollTopOffset={scrollTopOffset}
|
|
306
362
|
tableData={tableData}
|
|
307
363
|
hideColumns={hideColumns}
|
|
308
364
|
hideDetails={hideDetails}
|
|
@@ -39,6 +39,8 @@ export interface TableProps {
|
|
|
39
39
|
modalContentRenderer?: (content: ReactNode) => ReactNode;
|
|
40
40
|
onModalOpen?: ModalFunction;
|
|
41
41
|
onSelectionChanged?: (index: number) => void;
|
|
42
|
+
scrollOnOpen?: boolean;
|
|
43
|
+
scrollTopOffset?: number;
|
|
42
44
|
stickyHeaderTopOffset?: number;
|
|
43
45
|
tableData: TableData;
|
|
44
46
|
textOverrides?: TextOverrides;
|
|
@@ -63,6 +65,8 @@ const Table = ({
|
|
|
63
65
|
modalContentRenderer,
|
|
64
66
|
onModalOpen,
|
|
65
67
|
onSelectionChanged,
|
|
68
|
+
scrollOnOpen,
|
|
69
|
+
scrollTopOffset = 0,
|
|
66
70
|
stickyHeaderTopOffset = 0,
|
|
67
71
|
tableData,
|
|
68
72
|
textOverrides: definedTextOverrides,
|
|
@@ -76,6 +80,7 @@ const Table = ({
|
|
|
76
80
|
const [shouldHideDetails, setShouldHideDetails] = useState(true);
|
|
77
81
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
78
82
|
const headerRef = useRef<HTMLDivElement | null>(null);
|
|
83
|
+
const stickyHeaderRef = useRef<HTMLDivElement | null>(null);
|
|
79
84
|
const columnsLength = tableData[0].rows[0].length;
|
|
80
85
|
|
|
81
86
|
useScrollSync(headerRef, containerRef, !isMobile);
|
|
@@ -154,6 +159,7 @@ const Table = ({
|
|
|
154
159
|
</>
|
|
155
160
|
) : (
|
|
156
161
|
<div
|
|
162
|
+
ref={stickyHeaderRef}
|
|
157
163
|
aria-hidden
|
|
158
164
|
className={styles.stickyHeader}
|
|
159
165
|
style={{ top: `${stickyHeaderTopOffset}px` }}
|
|
@@ -179,6 +185,9 @@ const Table = ({
|
|
|
179
185
|
title={title}
|
|
180
186
|
className={className}
|
|
181
187
|
collapsibleSections={collapsibleSections}
|
|
188
|
+
scrollOnOpen={scrollOnOpen}
|
|
189
|
+
scrollTopOffset={scrollTopOffset}
|
|
190
|
+
stickyHeaderRef={stickyHeaderRef}
|
|
182
191
|
hideColumns={hideColumns}
|
|
183
192
|
hideDetails={hideDetails}
|
|
184
193
|
hideRows={hideRows}
|
|
@@ -4,9 +4,10 @@ import classNames from 'classnames';
|
|
|
4
4
|
interface CollapsibleProps {
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
isExpanded?: boolean;
|
|
7
|
+
onTransitionEnd?: () => void;
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export const Collapsible = ({ children, isExpanded }: CollapsibleProps) => {
|
|
10
|
+
export const Collapsible = ({ children, isExpanded, onTransitionEnd }: CollapsibleProps) => {
|
|
10
11
|
const [height, setHeight] = useState<number | undefined>();
|
|
11
12
|
|
|
12
13
|
const observerRef = useRef<ResizeObserver | null>(null);
|
|
@@ -45,6 +46,7 @@ export const Collapsible = ({ children, isExpanded }: CollapsibleProps) => {
|
|
|
45
46
|
style={{
|
|
46
47
|
maxHeight: isExpanded ? height : '0px',
|
|
47
48
|
}}
|
|
49
|
+
onTransitionEnd={isExpanded ? onTransitionEnd : undefined}
|
|
48
50
|
>
|
|
49
51
|
{children}
|
|
50
52
|
</div>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
1
|
+
import { MutableRefObject, useCallback, useRef, useState } from 'react';
|
|
2
2
|
import { TableSection } from '../TableSection/TableSection';
|
|
3
3
|
import { ChevronDownIcon, ChevronUpIcon } from '../../../icon';
|
|
4
4
|
import { Card } from '../../../cards/card';
|
|
@@ -12,6 +12,9 @@ import { IconRenderer } from '../IconRenderer/IconRenderer';
|
|
|
12
12
|
export interface TableContentsProps {
|
|
13
13
|
className?: string;
|
|
14
14
|
collapsibleSections?: boolean;
|
|
15
|
+
scrollOnOpen?: boolean;
|
|
16
|
+
scrollTopOffset?: number;
|
|
17
|
+
stickyHeaderRef?: MutableRefObject<HTMLDivElement | null>;
|
|
15
18
|
tableData: TableData;
|
|
16
19
|
hideColumns?: number[];
|
|
17
20
|
hideDetails?: boolean;
|
|
@@ -27,6 +30,9 @@ export interface TableContentsProps {
|
|
|
27
30
|
const TableContents = ({
|
|
28
31
|
className,
|
|
29
32
|
collapsibleSections,
|
|
33
|
+
scrollOnOpen,
|
|
34
|
+
scrollTopOffset = 0,
|
|
35
|
+
stickyHeaderRef,
|
|
30
36
|
tableData,
|
|
31
37
|
hideColumns = [],
|
|
32
38
|
hideDetails,
|
|
@@ -39,14 +45,33 @@ const TableContents = ({
|
|
|
39
45
|
imageComponent,
|
|
40
46
|
}: TableContentsProps) => {
|
|
41
47
|
const [isSectionOpen, setOpenSection] = useState<number | null>(null);
|
|
48
|
+
const lastToggledSection = useRef<number | null>(null);
|
|
49
|
+
const sectionRefs = useRef<Record<number, HTMLDivElement | null>>({});
|
|
42
50
|
const firstHeadRow = tableData?.[0]?.rows?.[0];
|
|
43
51
|
const tableWidth = isMobile ? `${firstHeadRow?.length * 50}%` : '';
|
|
44
52
|
const handleToggleSection = (index: number) => {
|
|
53
|
+
lastToggledSection.current = isSectionOpen === index ? null : index;
|
|
45
54
|
setOpenSection((currentSection) =>
|
|
46
55
|
currentSection === index ? null : index
|
|
47
56
|
);
|
|
48
57
|
};
|
|
49
58
|
|
|
59
|
+
const handleScrollToSection = useCallback(
|
|
60
|
+
(index: number) => {
|
|
61
|
+
if (scrollOnOpen && lastToggledSection.current === index && sectionRefs.current[index]) {
|
|
62
|
+
const headerHeight =
|
|
63
|
+
stickyHeaderRef?.current?.getBoundingClientRect().height ?? 0;
|
|
64
|
+
const top =
|
|
65
|
+
sectionRefs.current[index]!.getBoundingClientRect().top +
|
|
66
|
+
window.scrollY -
|
|
67
|
+
scrollTopOffset -
|
|
68
|
+
headerHeight;
|
|
69
|
+
window.scrollTo({ top, behavior: 'smooth' });
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
[scrollOnOpen, scrollTopOffset, stickyHeaderRef]
|
|
73
|
+
);
|
|
74
|
+
|
|
50
75
|
// Calculate global row offset for each section
|
|
51
76
|
let globalRowOffset = 0;
|
|
52
77
|
|
|
@@ -69,7 +94,7 @@ const TableContents = ({
|
|
|
69
94
|
.filter(localRowIndex => localRowIndex >= 0 && localRowIndex < rows.length);
|
|
70
95
|
|
|
71
96
|
const result = (isFirstSection || isVisible) && (
|
|
72
|
-
<div key={index}>
|
|
97
|
+
<div key={index} ref={(el) => { sectionRefs.current[index] = el; }}>
|
|
73
98
|
{section?.title && (
|
|
74
99
|
<div className={styles.cardWrapper}>
|
|
75
100
|
<div className={classNames(styles.card, 'p0')}>
|
|
@@ -103,7 +128,10 @@ const TableContents = ({
|
|
|
103
128
|
</div>
|
|
104
129
|
)}
|
|
105
130
|
|
|
106
|
-
<Collapsible
|
|
131
|
+
<Collapsible
|
|
132
|
+
isExpanded={isExpanded}
|
|
133
|
+
onTransitionEnd={() => handleScrollToSection(index)}
|
|
134
|
+
>
|
|
107
135
|
<TableSection
|
|
108
136
|
className={classNames(className, 'mb24')}
|
|
109
137
|
tableCellRows={
|