|
1 | | -import {Box, Colors, MiddleTruncate} from '@dagster-io/ui-components'; |
2 | | -import {useVirtualizer} from '@tanstack/react-virtual'; |
3 | | -import {useEffect, useRef} from 'react'; |
4 | | - |
5 | | -import {AssetListContainer, AssetListRow} from './AssetEventList'; |
6 | 1 | import {AssetPartitionStatus, assetPartitionStatusesToStyle} from './AssetPartitionStatus'; |
7 | | -import {Inner} from '../ui/VirtualizedTable'; |
| 2 | +import {PartitionListSelector, PartitionStatusDot} from './PartitionListSelector'; |
8 | 3 |
|
9 | 4 | export interface AssetPartitionListProps { |
10 | 5 | partitions: string[]; |
11 | 6 | statusForPartition: (dimensionKey: string) => AssetPartitionStatus[]; |
12 | 7 | focusedDimensionKey?: string; |
13 | 8 | setFocusedDimensionKey?: (dimensionKey: string | undefined) => void; |
14 | 9 | } |
| 10 | + |
| 11 | +const STATUS_ORDER: AssetPartitionStatus[] = [ |
| 12 | + AssetPartitionStatus.MISSING, |
| 13 | + AssetPartitionStatus.FAILED, |
| 14 | + AssetPartitionStatus.MATERIALIZING, |
| 15 | + AssetPartitionStatus.MATERIALIZED, |
| 16 | +]; |
| 17 | + |
15 | 18 | export const AssetPartitionList = ({ |
16 | 19 | focusedDimensionKey, |
17 | 20 | setFocusedDimensionKey, |
18 | 21 | statusForPartition, |
19 | 22 | partitions, |
20 | 23 | }: AssetPartitionListProps) => { |
21 | | - const parentRef = useRef<HTMLDivElement | null>(null); |
22 | | - |
23 | | - const rowVirtualizer = useVirtualizer({ |
24 | | - count: partitions.length, |
25 | | - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
26 | | - getItemKey: (idx) => partitions[idx]!, |
27 | | - getScrollElement: () => parentRef.current, |
28 | | - estimateSize: () => 36, |
29 | | - overscan: 10, |
30 | | - }); |
31 | | - const totalHeight = rowVirtualizer.getTotalSize(); |
32 | | - const items = rowVirtualizer.getVirtualItems(); |
33 | | - |
34 | | - useEffect(() => { |
35 | | - if (focusedDimensionKey && partitions.indexOf(focusedDimensionKey) !== -1) { |
36 | | - rowVirtualizer.scrollToIndex(partitions.indexOf(focusedDimensionKey), { |
37 | | - behavior: 'auto', |
38 | | - align: 'auto', |
39 | | - }); |
40 | | - } |
41 | | - }, [focusedDimensionKey, rowVirtualizer, partitions]); |
42 | | - |
43 | 24 | return ( |
44 | | - <AssetListContainer |
45 | | - ref={parentRef} |
46 | | - tabIndex={-1} |
47 | | - onKeyDown={(e) => { |
48 | | - const shift = {ArrowDown: 1, ArrowUp: -1}[e.key]; |
49 | | - if (!setFocusedDimensionKey || !shift || !focusedDimensionKey || e.isDefaultPrevented()) { |
50 | | - return; |
51 | | - } |
52 | | - const nextIdx = partitions.indexOf(focusedDimensionKey) + shift; |
53 | | - const next = partitions[nextIdx]; |
54 | | - if (next) { |
55 | | - e.preventDefault(); |
56 | | - setFocusedDimensionKey(next); |
57 | | - } |
58 | | - }} |
59 | | - > |
60 | | - <Inner $totalHeight={totalHeight}> |
61 | | - {items.map(({index, key, size, start}) => { |
62 | | - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion |
63 | | - const dimensionKey = partitions[index]!; |
64 | | - const state = statusForPartition(dimensionKey); |
65 | | - return ( |
66 | | - <AssetListRow |
67 | | - key={key} |
68 | | - $height={size} |
69 | | - $start={start} |
70 | | - $focused={dimensionKey === focusedDimensionKey} |
71 | | - onClick={(e) => { |
72 | | - // If you're interacting with something in the row, don't trigger a focus change. |
73 | | - // Since focus is stored in the URL bar this overwrites any link click navigation. |
74 | | - // We could alternatively e.preventDefault() on every link but it's easy to forget. |
75 | | - if (e.target instanceof HTMLElement && e.target.closest('a')) { |
76 | | - return; |
77 | | - } |
78 | | - setFocusedDimensionKey?.( |
79 | | - focusedDimensionKey !== dimensionKey ? dimensionKey : undefined, |
80 | | - ); |
81 | | - }} |
82 | | - > |
83 | | - <Box |
84 | | - style={{height: size}} |
85 | | - padding={{left: 24, right: 12}} |
86 | | - flex={{direction: 'column', justifyContent: 'center', gap: 8}} |
87 | | - border="bottom" |
88 | | - > |
89 | | - <Box flex={{gap: 4, direction: 'row', alignItems: 'center'}}> |
90 | | - <div |
91 | | - style={{flex: 1, minWidth: 0}} |
92 | | - data-tooltip={dimensionKey} |
93 | | - data-tooltip-style={PartitionTooltipStyle} |
94 | | - > |
95 | | - <MiddleTruncate text={dimensionKey} /> |
96 | | - </div> |
97 | | - {/* Note: we could just state.map, but we want these in a particular order*/} |
98 | | - {state.includes(AssetPartitionStatus.MISSING) && ( |
99 | | - <AssetPartitionStatusDot status={[AssetPartitionStatus.MISSING]} /> |
100 | | - )} |
101 | | - {state.includes(AssetPartitionStatus.FAILED) && ( |
102 | | - <AssetPartitionStatusDot status={[AssetPartitionStatus.FAILED]} /> |
103 | | - )} |
104 | | - {state.includes(AssetPartitionStatus.MATERIALIZING) && ( |
105 | | - <AssetPartitionStatusDot status={[AssetPartitionStatus.MATERIALIZING]} /> |
106 | | - )} |
107 | | - {state.includes(AssetPartitionStatus.MATERIALIZED) && ( |
108 | | - <AssetPartitionStatusDot status={[AssetPartitionStatus.MATERIALIZED]} /> |
109 | | - )} |
110 | | - </Box> |
111 | | - </Box> |
112 | | - </AssetListRow> |
113 | | - ); |
114 | | - })} |
115 | | - </Inner> |
116 | | - </AssetListContainer> |
| 25 | + <PartitionListSelector |
| 26 | + partitions={partitions} |
| 27 | + statusForPartition={statusForPartition} |
| 28 | + focusedDimensionKey={focusedDimensionKey} |
| 29 | + setFocusedDimensionKey={setFocusedDimensionKey} |
| 30 | + statusesToStyle={assetPartitionStatusesToStyle} |
| 31 | + statusOrder={STATUS_ORDER} |
| 32 | + /> |
117 | 33 | ); |
118 | 34 | }; |
119 | 35 |
|
120 | 36 | export const AssetPartitionStatusDot = ({status}: {status: AssetPartitionStatus[]}) => ( |
121 | | - <div |
122 | | - style={{ |
123 | | - width: 10, |
124 | | - height: 10, |
125 | | - borderRadius: '100%', |
126 | | - flexShrink: 0, |
127 | | - ...assetPartitionStatusesToStyle(status), |
128 | | - }} |
129 | | - /> |
| 37 | + <PartitionStatusDot status={status} statusesToStyle={assetPartitionStatusesToStyle} /> |
130 | 38 | ); |
131 | | - |
132 | | -const PartitionTooltipStyle = JSON.stringify({ |
133 | | - background: Colors.backgroundLight(), |
134 | | - border: `1px solid ${Colors.borderDefault()}`, |
135 | | - color: Colors.textDefault(), |
136 | | - fontSize: '14px', |
137 | | - top: 0, |
138 | | - left: 0, |
139 | | -}); |
0 commit comments