-
Notifications
You must be signed in to change notification settings - Fork 2
[Feature]: Improvements to the picker state management #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
e08ee11
2d94c58
3303a15
a33badf
92ea4b7
f4d6170
1b1293d
1de63db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| # Compose DateTimePicker | ||
|
|
||
| [](./README.md) | ||
|
|
||
| Compose Multiplatform을 위한 범용적이고 커스터마이징 가능한 날짜 및 시간 선택 라이브러리입니다. | ||
| Android, iOS, Desktop (JVM), Web (Wasm) 등 다양한 플랫폼에서 일관된 UI 컴포넌트를 제공합니다. | ||
|
|
||
| ## 주요 기능 | ||
|
|
||
| * **멀티플랫폼 지원**: Android, iOS, Desktop (JVM), Web (Wasm) 환경을 지원하며 원활한 통합이 가능합니다. | ||
| * **TimePicker**: 12시간(오전/오후) 및 24시간 형식을 모두 지원합니다. | ||
| * **YearMonthPicker**: 년도와 월을 선택할 수 있는 전용 컴포넌트를 제공합니다. | ||
| * **커스터마이징**: 커스텀 아이템 렌더링, 스타일링, 구성 변경이 가능한 유연한 API를 제공합니다. | ||
| * **상태 관리**: `rememberTimePickerState` 및 `rememberYearMonthPickerState`를 통해 간편하게 상태를 관리할 수 있습니다. | ||
| * **접근성**: 스크린 리더 및 내비게이션 지원 등 접근성을 고려하여 설계되었습니다. | ||
|
|
||
| ## 설치 방법 | ||
|
|
||
| 버전 카탈로그 또는 빌드 파일에 의존성을 추가하여 사용할 수 있습니다. | ||
|
|
||
| ### 버전 카탈로그 (libs.versions.toml) | ||
|
|
||
| ```toml | ||
| [versions] | ||
| composeDateTimePicker = "0.4.0" | ||
|
|
||
| [libraries] | ||
| compose-date-time-picker = { module = "io.github.kez-lab:compose-date-time-picker", version.ref = "composeDateTimePicker" } | ||
| ``` | ||
|
|
||
| ### Gradle (build.gradle.kts) | ||
|
|
||
| ```kotlin | ||
| dependencies { | ||
| implementation("io.github.kez-lab:compose-date-time-picker:0.4.0") | ||
| } | ||
| ``` | ||
|
|
||
| ## 사용법 | ||
|
|
||
| ### TimePicker | ||
|
|
||
| 시간 선택을 위해 `TimePicker`를 사용합니다. 12시간 및 24시간 형식을 지원합니다. | ||
|
|
||
| #### 1. 24시간 형식 | ||
|
|
||
| ```kotlin | ||
| import androidx.compose.runtime.Composable | ||
| import com.kez.picker.time.TimePicker | ||
| import com.kez.picker.rememberTimePickerState | ||
| import com.kez.picker.util.TimeFormat | ||
| import com.kez.picker.util.currentHour | ||
| import com.kez.picker.util.currentMinute | ||
|
|
||
| @Composable | ||
| fun TimePicker24hExample() { | ||
| val state = rememberTimePickerState( | ||
| initialHour = currentHour, | ||
| initialMinute = currentMinute, | ||
| timeFormat = TimeFormat.HOUR_24 | ||
| ) | ||
|
|
||
| TimePicker( | ||
| state = state | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| #### 2. 12시간 형식 (오전/오후) | ||
|
|
||
| ```kotlin | ||
| import androidx.compose.runtime.Composable | ||
| import com.kez.picker.time.TimePicker | ||
| import com.kez.picker.rememberTimePickerState | ||
| import com.kez.picker.util.TimeFormat | ||
| import com.kez.picker.util.TimePeriod | ||
| import com.kez.picker.util.currentHour | ||
| import com.kez.picker.util.currentMinute | ||
|
|
||
| @Composable | ||
| fun TimePicker12hExample() { | ||
| // 12시간 형식 변환은 이제 state 내부에서 처리됩니다. | ||
| val state = rememberTimePickerState( | ||
| initialHour = currentHour, | ||
| initialMinute = currentMinute, | ||
| timeFormat = TimeFormat.HOUR_12 | ||
| ) | ||
|
|
||
| TimePicker( | ||
| state = state | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| ### YearMonthPicker | ||
|
|
||
| 특정 연도와 월을 선택할 때 `YearMonthPicker`를 사용합니다. | ||
|
|
||
| ```kotlin | ||
| import androidx.compose.runtime.Composable | ||
| import com.kez.picker.date.YearMonthPicker | ||
| import com.kez.picker.rememberYearMonthPickerState | ||
| import com.kez.picker.util.currentDate | ||
|
|
||
| @Composable | ||
| fun YearMonthPickerExample() { | ||
| val state = rememberYearMonthPickerState( | ||
| initialYear = currentDate.year, | ||
| initialMonth = currentDate.monthNumber | ||
| ) | ||
|
|
||
| YearMonthPicker( | ||
| state = state | ||
| ) | ||
| } | ||
| ``` | ||
|
|
||
| ### BottomSheet 통합 | ||
|
|
||
| Picker 컴포넌트는 `ModalBottomSheet`나 다른 다이얼로그 컴포넌트 내에서도 원활하게 작동합니다. | ||
|
|
||
| ```kotlin | ||
| import androidx.compose.material3.* | ||
| import androidx.compose.runtime.* | ||
| import com.kez.picker.time.TimePicker | ||
| import com.kez.picker.rememberTimePickerState | ||
| import kotlinx.coroutines.launch | ||
|
|
||
| @OptIn(ExperimentalMaterial3Api::class) | ||
| @Composable | ||
| fun BottomSheetPickerExample() { | ||
| var showBottomSheet by remember { mutableStateOf(false) } | ||
| val sheetState = rememberModalBottomSheetState() | ||
| val state = rememberTimePickerState() | ||
| val scope = rememberCoroutineScope() | ||
|
|
||
| Button(onClick = { showBottomSheet = true }) { | ||
| Text("시간 선택") | ||
| } | ||
|
|
||
| if (showBottomSheet) { | ||
| ModalBottomSheet( | ||
| onDismissRequest = { showBottomSheet = false }, | ||
| sheetState = sheetState | ||
| ) { | ||
| TimePicker(state = state) | ||
| // 확인 버튼 로직 등 추가 가능 | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## API 레퍼런스 | ||
|
|
||
| ### TimePicker | ||
|
|
||
| | 파라미터 | 설명 | 기본값 | | ||
| | :--- | :--- | :--- | | ||
| | `state` | Picker를 제어하기 위한 상태 객체입니다. | `rememberTimePickerState()` | | ||
| | `startTime` | Picker에 설정될 초기 시간입니다. | `currentDateTime` | | ||
| | `visibleItemsCount` | 리스트에 표시될 아이템의 개수입니다. | `3` | | ||
| | `textStyle` | 선택되지 않은 아이템의 텍스트 스타일입니다. | `16.sp` | | ||
| | `selectedTextStyle` | 선택된 아이템의 텍스트 스타일입니다. | `22.sp` | | ||
| | `dividerColor` | 구분선의 색상입니다. | `LocalContentColor.current` | | ||
|
|
||
| ### YearMonthPicker | ||
|
|
||
| | 파라미터 | 설명 | 기본값 | | ||
| | :--- | :--- | :--- | | ||
| | `state` | Picker를 제어하기 위한 상태 객체입니다. | `rememberYearMonthPickerState()` | | ||
| | `startLocalDate` | Picker에 설정될 초기 날짜입니다. | `currentDate` | | ||
| | `yearItems` | 선택 가능한 연도 목록입니다. | `1900..2100` | | ||
| | `monthItems` | 선택 가능한 월 목록입니다. | `1..12` | | ||
| | `visibleItemsCount` | 리스트에 표시될 아이템의 개수입니다. | `3` | | ||
|
|
||
| ## 라이선스 | ||
|
|
||
| ``` | ||
| Copyright 2024 KEZ Lab | ||
|
|
||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. | ||
| You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,8 +75,8 @@ import kotlin.math.abs | |
| fun <T> Picker( | ||
| items: List<T>, | ||
| state: PickerState<T>, | ||
| startIndex: Int = 0, | ||
| modifier: Modifier = Modifier, | ||
| startIndex: Int = 0, | ||
| visibleItemsCount: Int = 3, | ||
| textStyle: TextStyle = LocalTextStyle.current, | ||
| selectedTextStyle: TextStyle = LocalTextStyle.current, | ||
|
|
@@ -94,7 +94,8 @@ fun <T> Picker( | |
| dividerThickness: Dp = 1.dp, | ||
| dividerShape: Shape = RoundedCornerShape(10.dp), | ||
| isDividerVisible: Boolean = true, | ||
| isInfinity: Boolean = true | ||
| isInfinity: Boolean = true, | ||
| content: @Composable ((T) -> Unit)? = null | ||
| ) { | ||
| val density = LocalDensity.current | ||
| val visibleItemsMiddle = remember { visibleItemsCount / 2 } | ||
|
|
@@ -205,14 +206,14 @@ fun <T> Picker( | |
| } | ||
| } | ||
|
|
||
| val currentItemText = getItem(index)?.toString().orEmpty() | ||
|
|
||
| val item = getItem(index) | ||
| Box( | ||
| modifier = Modifier | ||
| .height(itemHeight) | ||
| .fillMaxWidth() | ||
| .clickable( | ||
| enabled = getItem(index) != null, | ||
| enabled = item != null, | ||
| role = Role.Button, | ||
| indication = null, | ||
| interactionSource = remember { MutableInteractionSource() }, | ||
|
|
@@ -230,30 +231,35 @@ fun <T> Picker( | |
| .padding(itemPadding), | ||
| contentAlignment = Alignment.Center | ||
| ) { | ||
| Text( | ||
| text = currentItemText, | ||
| maxLines = 1, | ||
| overflow = TextOverflow.Ellipsis, | ||
| style = textStyle.copy( | ||
| fontSize = lerp( | ||
| selectedTextStyle.fontSize, | ||
| textStyle.fontSize, | ||
| fraction | ||
| ), | ||
| color = lerp( | ||
| selectedTextStyle.color, | ||
| textStyle.color, | ||
| fraction | ||
| ), | ||
| ), | ||
| textAlign = TextAlign.Center | ||
| ) | ||
| if (item != null) { | ||
| if (content != null) { | ||
| content(item) | ||
| } else { | ||
| Text( | ||
| text = item.toString(), | ||
| maxLines = 1, | ||
| overflow = TextOverflow.Ellipsis, | ||
| style = textStyle.copy( | ||
| fontSize = lerp( | ||
| selectedTextStyle.fontSize, | ||
| textStyle.fontSize, | ||
| fraction | ||
| ), | ||
| color = lerp( | ||
| selectedTextStyle.color, | ||
| textStyle.color, | ||
| fraction | ||
| ), | ||
| ), | ||
| textAlign = TextAlign.Center | ||
| ) | ||
| } | ||
| } | ||
|
Comment on lines
+234
to
+257
|
||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Apply a fading edge effect to a modifier. | ||
| * | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add language specifier to fenced code block.
The license text code block at Line 182 is missing a language specifier. Markdown linters expect fenced code blocks to declare a language.
Apply this diff to fix the code block:
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
182-182: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents