@@ -7,16 +7,18 @@ import NavigateNextIcon from "@mui/icons-material/NavigateNext";
77import WorkspacePremiumRoundedIcon from "@mui/icons-material/WorkspacePremiumRounded" ;
88import { Link } from "@mui/material" ;
99
10- import { useDataStore } from "store" ;
10+ import { useDataStore , useStorylineFilterStore } from "store" ;
1111
1212import { DIMENSIONS , getResponsiveChartWidth } from "./StorylineChart.const" ;
1313import "./StorylineChart.scss" ;
1414import { extractReleaseBasedContributorActivities } from "./StorylineChart.util" ;
1515import { renderReleaseVisualization } from "./ReleaseVisualization" ;
1616import { useFolderNavigation } from "./useFolderNavigation" ;
17+ import StorylineFilters from "./StorylineFilters" ;
1718
1819const StorylineChart = ( ) => {
1920 const [ totalData ] = useDataStore ( useShallow ( ( state ) => [ state . data ] ) ) ;
21+ const { releaseRange, selectedContributors } = useStorylineFilterStore ( ) ;
2022
2123 const svgRef = useRef < SVGSVGElement > ( null ) ;
2224 const tooltipRef = useRef < HTMLDivElement > ( null ) ;
@@ -39,6 +41,41 @@ const StorylineChart = () => {
3941
4042 const breadcrumbs = useMemo ( ( ) => getBreadcrumbs ( ) , [ getBreadcrumbs ] ) ;
4143
44+ // 원본 활동 데이터 계산
45+ const releaseContributorActivities = useMemo ( ( ) => {
46+ if ( ! totalData || totalData . length === 0 || releaseTopFolderPaths . length === 0 ) {
47+ return [ ] ;
48+ }
49+
50+ const currentDepth = currentPath === "" ? 1 : currentPath . split ( "/" ) . length + 1 ;
51+ return extractReleaseBasedContributorActivities ( totalData , releaseTopFolderPaths , currentDepth ) ;
52+ } , [ totalData , releaseTopFolderPaths , currentPath ] ) ;
53+
54+ // 필터링된 activities 계산
55+ const filteredActivities = useMemo ( ( ) => {
56+ if ( releaseContributorActivities . length === 0 ) {
57+ return [ ] ;
58+ }
59+
60+ let filtered = [ ...releaseContributorActivities ] ;
61+
62+ // 릴리즈 범위 필터
63+ if ( releaseRange ) {
64+ filtered = filtered . filter (
65+ ( activity ) =>
66+ activity . releaseIndex >= releaseRange . startReleaseIndex &&
67+ activity . releaseIndex <= releaseRange . endReleaseIndex
68+ ) ;
69+ }
70+
71+ // 기여자 필터
72+ if ( selectedContributors . length > 0 ) {
73+ filtered = filtered . filter ( ( activity ) => selectedContributors . includes ( activity . contributorName ) ) ;
74+ }
75+
76+ return filtered ;
77+ } , [ releaseContributorActivities , releaseRange , selectedContributors ] ) ;
78+
4279 useEffect ( ( ) => {
4380 const updateWidth = ( ) => {
4481 const containerWidth = containerRef . current ?. clientWidth ;
@@ -66,20 +103,6 @@ const StorylineChart = () => {
66103 } , [ ] ) ;
67104
68105 const { topContributorName, releaseRangeLabel } = useMemo ( ( ) => {
69- if ( ! totalData || totalData . length === 0 || releaseTopFolderPaths . length === 0 ) {
70- return {
71- topContributorName : null ,
72- releaseRangeLabel : "..." ,
73- } ;
74- }
75-
76- const currentDepth = currentPath === "" ? 1 : currentPath . split ( "/" ) . length + 1 ;
77- const releaseContributorActivities = extractReleaseBasedContributorActivities (
78- totalData ,
79- releaseTopFolderPaths ,
80- currentDepth
81- ) ;
82-
83106 if ( releaseContributorActivities . length === 0 ) {
84107 return {
85108 topContributorName : null ,
@@ -125,7 +148,7 @@ const StorylineChart = () => {
125148 topContributorName : mostActiveContributor || null ,
126149 releaseRangeLabel : rangeLabel || "..." ,
127150 } ;
128- } , [ totalData , releaseTopFolderPaths , currentPath ] ) ;
151+ } , [ releaseContributorActivities ] ) ;
129152
130153 useEffect ( ( ) => {
131154 if ( ! totalData || totalData . length === 0 ) {
@@ -142,17 +165,9 @@ const StorylineChart = () => {
142165
143166 const svg = d3 . select ( svgRef . current ) . attr ( "width" , chartWidth ) . attr ( "height" , DIMENSIONS . height ) ;
144167
145- // activity가 있는 폴더 카운트
146- const currentDepth = currentPath === "" ? 1 : currentPath . split ( "/" ) . length + 1 ;
147- const releaseContributorActivities = extractReleaseBasedContributorActivities (
148- totalData ,
149- releaseTopFolderPaths ,
150- currentDepth
151- ) ;
152-
153168 svg . selectAll ( "*" ) . remove ( ) ;
154169
155- if ( releaseContributorActivities . length === 0 ) {
170+ if ( filteredActivities . length === 0 ) {
156171 svg
157172 . append ( "text" )
158173 . attr ( "x" , chartWidth / 2 )
@@ -167,12 +182,12 @@ const StorylineChart = () => {
167182
168183 renderReleaseVisualization ( {
169184 svg,
170- releaseContributorActivities,
185+ releaseContributorActivities : filteredActivities ,
171186 releaseTopFolderPaths,
172187 tooltipRef,
173188 onFolderClick : navigateToFolder ,
174189 } ) ;
175- } , [ totalData , releaseGroups , releaseTopFolderPaths , navigateToFolder , currentPath , chartWidth ] ) ;
190+ } , [ filteredActivities , releaseTopFolderPaths , navigateToFolder , chartWidth , totalData , releaseGroups ] ) ;
176191
177192 const topContributorLabel = topContributorName || "..." ;
178193
@@ -218,6 +233,9 @@ const StorylineChart = () => {
218233
219234 < div className = "storyline-chart__subtitle" > { releaseRangeLabel } </ div >
220235 </ div >
236+ < div className = "storyline-chart__filters" >
237+ { releaseContributorActivities . length > 0 && < StorylineFilters activities = { releaseContributorActivities } /> }
238+ </ div >
221239
222240 < svg
223241 className = "storyline-chart__chart"
0 commit comments