This commit is contained in:
2026-01-13 16:04:25 +09:00
parent d4770af62f
commit 2bb94e2856
23 changed files with 1406 additions and 1103 deletions
+101
View File
@@ -0,0 +1,101 @@
// src/main/kotlin/ui/MarketSection.kt
package ui
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import model.RankingStock
import model.RankingType
import network.KisTradeService
@Composable
fun MarketSection(
tradeService: KisTradeService,
onStockSelect: (code: String, name: String, isDomestic: Boolean) -> Unit
) {
var selectedTab by remember { mutableStateOf(RankingType.VOLUME) }
var isDomestic by remember { mutableStateOf(true) }
var rankingList by remember { mutableStateOf<List<RankingStock>>(emptyList()) }
var isLoading by remember { mutableStateOf(false) }
// 탭 또는 국가 변경 시 데이터 로드
LaunchedEffect(selectedTab, isDomestic) {
isLoading = true
tradeService.fetchMarketRanking(selectedTab, isDomestic).onSuccess {
rankingList = it
}.onFailure {
rankingList = emptyList()
}
isLoading = false
}
Column(modifier = Modifier.fillMaxSize()) {
// [1] 상단 타이틀 및 국내/해외 토글
Row(
modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("시장 랭킹 (TOP 20)", style = MaterialTheme.typography.subtitle1, fontWeight = FontWeight.Bold)
// 국내/해외 전환 스위치 방식
Row(verticalAlignment = Alignment.CenterVertically) {
Text(if (isDomestic) "국내" else "해외", fontSize = 12.sp, fontWeight = FontWeight.Medium)
Switch(
checked = !isDomestic,
onCheckedChange = { isDomestic = !it },
colors = SwitchDefaults.colors(checkedThumbColor = Color(0xFF0E62CF))
)
}
}
// [2] 랭킹 타입 탭 (상승, 하락, 거래량 등)
ScrollableTabRow(
selectedTabIndex = selectedTab.ordinal,
backgroundColor = Color.Transparent,
contentColor = Color.Black,
edgePadding = 0.dp,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
Modifier.tabIndicatorOffset(tabPositions[selectedTab.ordinal]),
color = Color(0xFFE03E2D)
)
}
) {
RankingType.values().forEach { type ->
Tab(
selected = selectedTab == type,
onClick = { selectedTab = type },
text = { Text(type.title, fontSize = 12.sp) }
)
}
}
Spacer(modifier = Modifier.height(8.dp))
// [3] 랭킹 리스트
if (isLoading) {
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
CircularProgressIndicator(strokeWidth = 2.dp)
}
} else {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(rankingList.withIndex().toList()) { (index, stock) ->
MarketStockItemRow(index + 1, stock) {
onStockSelect(stock.code, stock.name, isDomestic)
}
Divider(color = Color(0xFFF5F5F5), thickness = 0.5.dp)
}
}
}
}
}