101 lines
3.7 KiB
Kotlin
101 lines
3.7 KiB
Kotlin
// 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)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|