diff --git a/README.md b/README.md index f032a87..3db8d59 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,34 @@ # Compose DateTimePicker -[![Maven Central](https://img.shields.io/maven-central/v/io.github.kez-lab/compose-date-time-picker.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/io.github.kez-lab/compose-date-time-picker) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -[![Kotlin](https://img.shields.io/badge/Kotlin-2.2.21-purple.svg?logo=kotlin)](http://kotlinlang.org) -[![Compose Multiplatform](https://img.shields.io/badge/Compose%20Multiplatform-1.10.0--beta01-brightgreen)](https://github.com/JetBrains/compose-multiplatform) +[![Read in Korean](https://img.shields.io/badge/README-Korean-green)](./README_KO.md) -A Compose Multiplatform date and time picker library that provides a consistent UI across Android, iOS, Desktop, and Web platforms. +A generic, customizable, and multiplatform date and time picker library for Compose Multiplatform. +It provides consistent UI components across Android, iOS, Desktop (JVM), and Web (Wasm). -[πŸ“ Blog: Development Journey (Korean)](https://velog.io/@kej_ad/AndroidCompose-Year-Month-DatePicker-%EB%A7%8C%EB%93%A4%EA%B8%B0) +## Features ---- +* **Multiplatform Support**: seamless integration for Android, iOS, Desktop (JVM), and Web (Wasm). +* **TimePicker**: Supports both 12-hour (AM/PM) and 24-hour formats. +* **YearMonthPicker**: A dedicated component for selecting years and months. +* **Customizable**: Extensible API allowing custom content rendering, styling, and configuration. +* **State Management**: simplified state handling with `rememberTimePickerState` and `rememberYearMonthPickerState`. +* **Accessibility**: Built with accessibility in mind, supporting screen readers and navigation. -## πŸ“± Screenshots +## Installation -### Sample Screens +Add the dependency to your version catalog or build file. - - - - - -
-
- TimePicker Sample -
-
- DatePicker Sample -
+### Version Catalog (libs.versions.toml) -### BottomSheet Integration - - - - - -
-
- BottomSheet Sample
- Date and time selection in bottom sheet -
- -> **Demo**: The animation above shows the complete interaction flow for date and time selection using bottom sheets. - ---- - -## ✨ Features - -- 🌐 **Multiplatform**: Android, iOS, Desktop (JVM), and Web support -- ⏰ **TimePicker**: 12-hour and 24-hour format support -- πŸ“… **YearMonthPicker**: Year and month selection component -- πŸ“± **BottomSheet Ready**: Works seamlessly with Material3 ModalBottomSheet -- 🎨 **Fully Customizable**: Colors, fonts, sizes, visible item counts, and more -- πŸ“ **Responsive Layout**: Automatically adapts to screen sizes -- 🧩 **Compose Native**: Seamless integration with Jetpack Compose and Compose Multiplatform - -### Why Use This Library? - -- **Consistent UX**: Same design and behavior across all platforms -- **KMP Optimized**: Built on kotlinx-datetime for stable cross-platform operation -- **Extensible**: Build custom pickers using the provided base components +```toml +[versions] +composeDateTimePicker = "0.4.0" ---- - -## πŸ“¦ Installation +[libraries] +compose-date-time-picker = { module = "io.github.kez-lab:compose-date-time-picker", version.ref = "composeDateTimePicker" } +``` -### Gradle (Kotlin DSL) +### Gradle (build.gradle.kts) ```kotlin dependencies { @@ -72,144 +36,102 @@ dependencies { } ``` -### Platform Support +## Usage -| Platform | Status | Minimum Version | -|----------|--------|-----------------| -| Android | βœ… Stable | API 24+ | -| iOS | πŸ§ͺ Beta | iOS 13+ | -| Desktop (JVM) | βœ… Stable | JVM 17+ | -| Web (JS) | ⚠️ Experimental | Modern browsers | +### TimePicker ---- +Use `TimePicker` for time selection. It supports both 12-hour and 24-hour formats. -## πŸš€ Quick Start - -### 1. TimePicker (24-Hour Format) +#### 1. 24-Hour Format ```kotlin -import androidx.compose.runtime.* -import com.kez.picker.rememberPickerState +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 TimePickerExample() { - // Manage picker state - val hourState = rememberPickerState(currentHour) - val minuteState = rememberPickerState(currentMinute) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth().padding(16.dp) - ) { - TimePicker( - hourPickerState = hourState, - minutePickerState = minuteState, - timeFormat = TimeFormat.HOUR_24 - ) - - Spacer(modifier = Modifier.height(16.dp)) - - // Display selected time - Text( - text = "Selected: %02d:%02d".format( - hourState.selectedItem, - minuteState.selectedItem - ), - style = MaterialTheme.typography.titleLarge - ) - } +fun TimePicker24hExample() { + val state = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute, + timeFormat = TimeFormat.HOUR_24 + ) + + TimePicker( + state = state + ) } ``` -### 2. TimePicker (12-Hour Format with AM/PM) +#### 2. 12-Hour Format (AM/PM) ```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 TimePicker12HourExample() { - // Convert current time to 12-hour format - val hour12 = if (currentHour > 12) currentHour - 12 - else if (currentHour == 0) 12 - else currentHour - val period = if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM - - val hourState = rememberPickerState(hour12) - val minuteState = rememberPickerState(currentMinute) - val periodState = rememberPickerState(period) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth().padding(16.dp) - ) { - TimePicker( - hourPickerState = hourState, - minutePickerState = minuteState, - periodPickerState = periodState, - timeFormat = TimeFormat.HOUR_12 // Enable 12-hour format - ) - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "%02d:%02d %s".format( - hourState.selectedItem, - minuteState.selectedItem, - periodState.selectedItem // AM or PM - ), - style = MaterialTheme.typography.titleLarge - ) - } +fun TimePicker12hExample() { + // Handling of 12-hour format conversion is now done internally by the state + val state = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute, + timeFormat = TimeFormat.HOUR_12 + ) + + TimePicker( + state = state + ) } ``` -### 3. YearMonthPicker +### YearMonthPicker + +Use `YearMonthPicker` for selecting a specific month in a year. ```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() { - // Using kotlinx-datetime (KMP standard) - val yearState = rememberPickerState(currentDate.year) - val monthState = rememberPickerState(currentDate.monthNumber) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth().padding(16.dp) - ) { - YearMonthPicker( - yearPickerState = yearState, - monthPickerState = monthState - ) - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "Selected: ${yearState.selectedItem}/${monthState.selectedItem}", - style = MaterialTheme.typography.titleLarge - ) - } + val state = rememberYearMonthPickerState( + initialYear = currentDate.year, + initialMonth = currentDate.monthNumber + ) + + YearMonthPicker( + state = state + ) } ``` -### 4. BottomSheet Integration +### BottomSheet Integration + +The pickers work seamlessly within a `ModalBottomSheet` or other dialog components. ```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 hourState = rememberPickerState(currentHour) - val minuteState = rememberPickerState(currentMinute) + val state = rememberTimePickerState() val scope = rememberCoroutineScope() Button(onClick = { showBottomSheet = true }) { @@ -221,171 +143,37 @@ fun BottomSheetPickerExample() { onDismissRequest = { showBottomSheet = false }, sheetState = sheetState ) { - Column( - modifier = Modifier.fillMaxWidth().padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - TimePicker( - hourPickerState = hourState, - minutePickerState = minuteState, - timeFormat = TimeFormat.HOUR_24 - ) - - Button( - onClick = { - scope.launch { - sheetState.hide() - showBottomSheet = false - } - } - ) { - Text("Confirm") - } - } + TimePicker(state = state) + // Add confirmation buttons logic here } } } ``` ---- - -## πŸ“– API Reference - -### TimePicker Parameters - -| Parameter | Type | Default | Description | -| --- | --- | --- | --- | -| `modifier` | `Modifier` | `Modifier` | Modifier to be applied to the composable | -| `hourPickerState` | `PickerState` | `rememberPickerState(currentHour)` | State for the hour picker | -| `minutePickerState` | `PickerState` | `rememberPickerState(currentMinute)` | State for the minute picker | -| `periodPickerState` | `PickerState?` | `null` | State for AM/PM picker (12-hour format only) | -| `timeFormat` | `TimeFormat` | `TimeFormat.HOUR_24` | Time format (`HOUR_12` or `HOUR_24`) | -| `startTime` | `LocalDateTime` | `currentDateTime` | Initial time value | -| `minuteItems` | `List` | `0..59` | List of minutes to display | -| `hourItems` | `List` | `1..12` (12-hour) / `0..23` (24-hour) | List of hours to display | -| `periodItems` | `List` | `[AM, PM]` | List of periods (12-hour format only) | -| `visibleItemsCount` | `Int` | `3` | Number of items visible at once | -| `itemPadding` | `PaddingValues` | `PaddingValues(8.dp)` | Padding for each item | -| `textStyle` | `TextStyle` | `TextStyle(fontSize = 16.sp)` | Text style for unselected items | -| `selectedTextStyle` | `TextStyle` | `TextStyle(fontSize = 22.sp)` | Text style for the selected item | -| `dividerColor` | `Color` | `LocalContentColor.current` | Color of the divider line | -| `fadingEdgeGradient` | `Brush` | (default gradient) | Gradient for fading edges | -| `dividerThickness` | `Dp` | `1.dp` | Thickness of the divider line | -| `pickerWidth` | `Dp` | `80.dp` | Width of each picker column | - -### YearMonthPicker Parameters - -| Parameter | Type | Default | Description | -| --- | --- | --- | --- | -| `modifier` | `Modifier` | `Modifier` | Modifier to be applied to the composable | -| `yearPickerState` | `PickerState` | `rememberPickerState(currentDate.year)` | State for the year picker | -| `monthPickerState` | `PickerState` | `rememberPickerState(currentDate.monthNumber)` | State for the month picker | -| `startLocalDate` | `LocalDate` | `currentDate` | Initial date value | -| `yearItems` | `List` | `1900..2100` | List of years to display | -| `monthItems` | `List` | `1..12` | List of months to display | -| `visibleItemsCount` | `Int` | `3` | Number of items visible at once | -| `itemPadding` | `PaddingValues` | `PaddingValues(8.dp)` | Padding for each item | -| `textStyle` | `TextStyle` | `TextStyle(fontSize = 16.sp)` | Text style for unselected items | -| `selectedTextStyle` | `TextStyle` | `TextStyle(fontSize = 24.sp)` | Text style for the selected item | -| `dividerColor` | `Color` | `LocalContentColor.current` | Color of the divider line | -| `fadingEdgeGradient` | `Brush` | (default gradient) | Gradient for fading edges | -| `dividerThickness` | `Dp` | `2.dp` | Thickness of the divider line | -| `pickerWidth` | `Dp` | `100.dp` | Width of each picker column | - -### Custom Picker Extension - -You can extend the library by using the `Picker` composable directly to create custom pickers: - -```kotlin -@Composable -fun CustomPicker() { - Picker( - state = rememberPickerState(0), - items = listOf("Option 1", "Option 2", "Option 3"), - // ... customization parameters - ) -} -``` - ---- - -## πŸ’» Running the Sample Project - -### Android - -```bash -./gradlew :sample:installDebug -``` - -The app will be installed on your connected device or emulator. - -### Desktop (JVM) - -In Android Studio or IntelliJ IDEA: -1. Open `sample/src/desktopMain/kotlin/com/kez/picker/sample/Main.kt` -2. Click the run button next to the `main()` function - -Or run via Gradle: -```bash -./gradlew :sample:desktopRun -DmainClass=com.kez.picker.sample.MainKt --quiet -``` - -### iOS +## API Reference -Open the `iosApp/` project in Xcode and run. +### TimePicker -### Web +| Parameter | Description | Default | +| :--- | :--- | :--- | +| `state` | The state object to control the picker. | `rememberTimePickerState()` | +| `startTime` | The initial time to set the picker to. | `currentDateTime` | +| `visibleItemsCount` | Number of items visible in the list. | `3` | +| `textStyle` | Style for unselected items. | `16.sp` | +| `selectedTextStyle` | Style for selected item. | `22.sp` | +| `dividerColor` | Color of the selection dividers. | `LocalContentColor.current` | -Build and serve the web application: -```bash -./gradlew :sample:wasmJsBrowserDevelopmentRun -``` - ---- - -## 🀝 Contributing - -Contributions are welcome! Whether it's bug reports, feature requests, or code contributions, we appreciate your help. - -### How to Contribute - -1. Fork this repository -2. Create a feature branch (`git checkout -b feature/amazing-feature`) -3. Commit your changes (`git commit -m 'Add some amazing feature'`) -4. Push to the branch (`git push origin feature/amazing-feature`) -5. Open a Pull Request - -For bugs or feature requests, please use [GitHub Issues](https://github.com/kez-lab/Compose-DateTimePicker/issues). - ---- - -## πŸ“ Project Structure - -```text -Compose-DateTimePicker/ -β”œβ”€β”€ datetimepicker/ # Library module -β”‚ └── src/ -β”‚ β”œβ”€β”€ commonMain/ # Shared code -β”‚ β”‚ └── kotlin/com/kez/picker/ -β”‚ β”‚ β”œβ”€β”€ date/ # Date pickers (YearMonthPicker) -β”‚ β”‚ β”œβ”€β”€ time/ # Time pickers (TimePicker) -β”‚ β”‚ └── util/ # Utilities (currentHour, currentDate, etc.) -β”‚ β”œβ”€β”€ androidMain/ # Android platform implementation -β”‚ β”œβ”€β”€ iosMain/ # iOS platform implementation -β”‚ β”œβ”€β”€ desktopMain/ # Desktop (JVM) platform implementation -β”‚ └── jsMain/ # Web (JS) platform implementation -└── sample/ # Sample application - └── src/ - β”œβ”€β”€ commonMain/ # Shared sample code - β”œβ”€β”€ androidMain/ # Android entry point - β”œβ”€β”€ iosMain/ # iOS entry point - β”œβ”€β”€ jvmMain/ # Desktop entry point - └── jsMain/ # Web entry point -``` +### YearMonthPicker ---- +| Parameter | Description | Default | +| :--- | :--- | :--- | +| `state` | The state object to control the picker. | `rememberYearMonthPickerState()` | +| `startLocalDate` | The initial date to set the picker to. | `currentDate` | +| `yearItems` | List of years available for selection. | `1900..2100` | +| `monthItems` | List of months available for selection. | `1..12` | +| `visibleItemsCount` | Number of items visible in the list. | `3` | -## πŸ“„ License +## License ``` Copyright 2024 KEZ Lab @@ -402,11 +190,3 @@ 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. ``` - ---- - -## πŸ™ Acknowledgements - -Special thanks to all contributors and the Compose Multiplatform community. - -If you find this library helpful, please consider giving it a ⭐️ on GitHub! diff --git a/README_KO.md b/README_KO.md new file mode 100644 index 0000000..3b13865 --- /dev/null +++ b/README_KO.md @@ -0,0 +1,192 @@ +# Compose DateTimePicker + +[![Read in English](https://img.shields.io/badge/README-English-blue)](./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. +``` diff --git a/datetimepicker/src/commonMain/kotlin/com/kez/picker/Picker.kt b/datetimepicker/src/commonMain/kotlin/com/kez/picker/Picker.kt index d6d8e06..f751435 100644 --- a/datetimepicker/src/commonMain/kotlin/com/kez/picker/Picker.kt +++ b/datetimepicker/src/commonMain/kotlin/com/kez/picker/Picker.kt @@ -75,8 +75,8 @@ import kotlin.math.abs fun Picker( items: List, state: PickerState, - 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 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 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 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 + ) + } + } } } } } } - /** * Apply a fading edge effect to a modifier. * diff --git a/datetimepicker/src/commonMain/kotlin/com/kez/picker/PickerState.kt b/datetimepicker/src/commonMain/kotlin/com/kez/picker/PickerState.kt index ca6206d..8b316d7 100644 --- a/datetimepicker/src/commonMain/kotlin/com/kez/picker/PickerState.kt +++ b/datetimepicker/src/commonMain/kotlin/com/kez/picker/PickerState.kt @@ -1,14 +1,21 @@ package com.kez.picker import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import com.kez.picker.util.TimeFormat +import com.kez.picker.util.TimePeriod +import com.kez.picker.util.currentDate +import com.kez.picker.util.currentHour +import com.kez.picker.util.currentMinute +import kotlinx.datetime.number /** * Remember a [PickerState] with the given initial item. - * + * * @param initialItem The initial selected item. * @return A [PickerState] with the given initial item. */ @@ -20,6 +27,7 @@ fun rememberPickerState(initialItem: T) = remember { PickerState(initialItem * * @param initialItem The initial selected item. */ +@Stable class PickerState( initialItem: T ) { @@ -27,4 +35,110 @@ class PickerState( * The currently selected item. */ var selectedItem by mutableStateOf(initialItem) -} \ No newline at end of file +} + +/** + * Creates and remembers a [YearMonthPickerState]. + * + * @param initialYear The initial year to be selected. Defaults to the current year. + * @param initialMonth The initial month to be selected. Defaults to the current month. + * @return A [YearMonthPickerState] initialized with the given year and month. + */ +@Composable +fun rememberYearMonthPickerState( + initialYear: Int = currentDate.year, + initialMonth: Int = currentDate.month.number +): YearMonthPickerState { + return remember(initialYear, initialMonth) { + YearMonthPickerState(initialYear, initialMonth) + } +} + +/** + * State holder for the [com.kez.picker.date.YearMonthPicker]. + * + * Manages the state of the year and month pickers. + * + * @param initialYear The initial year to be selected. + * @param initialMonth The initial month to be selected. + */ +@Stable +class YearMonthPickerState( + initialYear: Int, + initialMonth: Int +) { + val yearState = PickerState(initialYear) + val monthState = PickerState(initialMonth) + + val selectedYear: Int + get() = yearState.selectedItem + + val selectedMonth: Int + get() = monthState.selectedItem +} + +/** + * Creates and remembers a [TimePickerState]. + * + * @param initialHour The initial hour to be selected. Defaults to the current hour. + * If [timeFormat] is [TimeFormat.HOUR_12], this value is automatically adjusted to the 12-hour format (1-12). + * @param initialMinute The initial minute to be selected. Defaults to the current minute. + * @param initialPeriod The initial period (AM/PM) to be selected. Defaults to the current period based on the current hour. + * @param timeFormat The time format (12-hour or 24-hour). Defaults to [TimeFormat.HOUR_24]. + * @return A [TimePickerState] initialized with the given time values. + */ +@Composable +fun rememberTimePickerState( + initialHour: Int = currentHour, + initialMinute: Int = currentMinute, + initialPeriod: TimePeriod = if (initialHour >= 12) TimePeriod.PM else TimePeriod.AM, + timeFormat: TimeFormat = TimeFormat.HOUR_24 +): TimePickerState { + val adjustedHour = remember(initialHour, timeFormat) { + if (timeFormat == TimeFormat.HOUR_12) { + val h = initialHour % 12 + if (h == 0) 12 else h + } else { + initialHour + } + } + return remember(adjustedHour, initialMinute, initialPeriod, timeFormat) { + TimePickerState( + initialHour = adjustedHour, + initialMinute = initialMinute, + initialPeriod = initialPeriod, + timeFormat = timeFormat, + ) + } +} + +/** + * State holder for the [TimePicker]. + * + * Manages the state of the hour, minute, and period (AM/PM) pickers. + * + * @param initialHour The initial hour to be selected. + * @param initialMinute The initial minute to be selected. + * @param initialPeriod The initial period (AM/PM) to be selected. + * @param timeFormat The time format (12-hour or 24-hour). + */ +@Stable +class TimePickerState( + initialHour: Int, + initialMinute: Int, + initialPeriod: TimePeriod, + val timeFormat: TimeFormat +) { + val hourState = PickerState(initialHour) + val minuteState = PickerState(initialMinute) + val periodState = PickerState(initialPeriod) + + val selectedHour: Int + get() = hourState.selectedItem + + val selectedMinute: Int + get() = minuteState.selectedItem + + val selectedPeriod: TimePeriod + get() = periodState.selectedItem +} \ No newline at end of file diff --git a/datetimepicker/src/commonMain/kotlin/com/kez/picker/date/YearMonthPicker.kt b/datetimepicker/src/commonMain/kotlin/com/kez/picker/date/YearMonthPicker.kt index 58aa2d8..07ec07d 100644 --- a/datetimepicker/src/commonMain/kotlin/com/kez/picker/date/YearMonthPicker.kt +++ b/datetimepicker/src/commonMain/kotlin/com/kez/picker/date/YearMonthPicker.kt @@ -21,8 +21,8 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kez.picker.Picker -import com.kez.picker.PickerState -import com.kez.picker.rememberPickerState +import com.kez.picker.YearMonthPickerState +import com.kez.picker.rememberYearMonthPickerState import com.kez.picker.util.MONTH_RANGE import com.kez.picker.util.YEAR_RANGE import com.kez.picker.util.currentDate @@ -58,8 +58,7 @@ import kotlinx.datetime.number fun YearMonthPicker( modifier: Modifier = Modifier, pickerModifier: Modifier = Modifier, - yearPickerState: PickerState = rememberPickerState(currentDate.year), - monthPickerState: PickerState = rememberPickerState(currentDate.month.number), + state: YearMonthPickerState = rememberYearMonthPickerState(), startLocalDate: LocalDate = currentDate, yearItems: List = YEAR_RANGE, monthItems: List = MONTH_RANGE, @@ -104,7 +103,7 @@ fun YearMonthPicker( ), ) { Picker( - state = yearPickerState, + state = state.yearState, modifier = pickerModifier.weight(1f), items = yearItems, startIndex = yearStartIndex, @@ -123,7 +122,7 @@ fun YearMonthPicker( isDividerVisible = isDividerVisible, ) Picker( - state = monthPickerState, + state = state.monthState, items = monthItems, startIndex = monthStartIndex, visibleItemsCount = visibleItemsCount, diff --git a/datetimepicker/src/commonMain/kotlin/com/kez/picker/time/TimePicker.kt b/datetimepicker/src/commonMain/kotlin/com/kez/picker/time/TimePicker.kt index 4a2d7de..75a00ec 100644 --- a/datetimepicker/src/commonMain/kotlin/com/kez/picker/time/TimePicker.kt +++ b/datetimepicker/src/commonMain/kotlin/com/kez/picker/time/TimePicker.kt @@ -23,15 +23,14 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kez.picker.Picker -import com.kez.picker.PickerState +import com.kez.picker.TimePickerState +import com.kez.picker.rememberTimePickerState import com.kez.picker.util.HOUR12_RANGE import com.kez.picker.util.HOUR24_RANGE import com.kez.picker.util.MINUTE_RANGE import com.kez.picker.util.TimeFormat import com.kez.picker.util.TimePeriod import com.kez.picker.util.currentDateTime -import com.kez.picker.util.currentHour -import com.kez.picker.util.currentMinute import kotlinx.datetime.LocalDateTime /** @@ -39,10 +38,7 @@ import kotlinx.datetime.LocalDateTime * * @param modifier The modifier to be applied to the component. * @param pickerModifier The modifier to be applied to each picker. - * @param minutePickerState The state for the minute picker. - * @param hourPickerState The state for the hour picker. - * @param periodPickerState The state for the AM/PM period picker. - * @param timeFormat The time format (12-hour or 24-hour). + * @param state The state object to control the picker. * @param startTime The initial time to display. * @param minuteItems The list of minute values to display. * @param hourItems The list of hour values to display. @@ -66,13 +62,10 @@ import kotlinx.datetime.LocalDateTime fun TimePicker( modifier: Modifier = Modifier, pickerModifier: Modifier = Modifier, - minutePickerState: PickerState = remember { PickerState(currentMinute) }, - hourPickerState: PickerState = remember { PickerState(currentHour) }, - periodPickerState: PickerState = remember { PickerState(TimePeriod.AM) }, - timeFormat: TimeFormat = TimeFormat.HOUR_24, + state: TimePickerState = rememberTimePickerState(), startTime: LocalDateTime = currentDateTime, minuteItems: List = MINUTE_RANGE, - hourItems: List = when (timeFormat) { + hourItems: List = when (state.timeFormat) { TimeFormat.HOUR_12 -> HOUR12_RANGE TimeFormat.HOUR_24 -> HOUR24_RANGE }, @@ -108,7 +101,7 @@ fun TimePicker( } val hourStartIndex = remember { - val startHour = when (timeFormat) { + val startHour = when (state.timeFormat) { TimeFormat.HOUR_12 -> { val hour = startTime.hour % 12 if (hour == 0) 12 else hour @@ -129,9 +122,9 @@ fun TimePicker( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { - if (timeFormat == TimeFormat.HOUR_12) { + if (state.timeFormat == TimeFormat.HOUR_12) { Picker( - state = periodPickerState, + state = state.periodState, items = periodItems, visibleItemsCount = visibleItemsCount, modifier = pickerModifier.weight(1f), @@ -153,7 +146,7 @@ fun TimePicker( Spacer(modifier = Modifier.width(spacingBetweenPickers)) } Picker( - state = hourPickerState, + state = state.hourState, modifier = pickerModifier.weight(1f), items = hourItems, startIndex = hourStartIndex, @@ -173,7 +166,7 @@ fun TimePicker( ) Spacer(modifier = Modifier.width(spacingBetweenPickers)) Picker( - state = minutePickerState, + state = state.minuteState, items = minuteItems, startIndex = minuteStartIndex, visibleItemsCount = visibleItemsCount, @@ -200,7 +193,7 @@ fun TimePicker( @Composable fun TimePickerPreview24Hour() { TimePicker( - timeFormat = TimeFormat.HOUR_24 + state = rememberTimePickerState(timeFormat = TimeFormat.HOUR_24) ) } @@ -208,7 +201,7 @@ fun TimePickerPreview24Hour() { @Composable fun TimePickerPreview12Hour() { TimePicker( - timeFormat = TimeFormat.HOUR_12 + state = rememberTimePickerState(timeFormat = TimeFormat.HOUR_12) ) } @@ -216,7 +209,7 @@ fun TimePickerPreview12Hour() { @Composable fun TimePickerNoDividerPreview() { TimePicker( - timeFormat = TimeFormat.HOUR_24, + state = rememberTimePickerState(timeFormat = TimeFormat.HOUR_24), isDividerVisible = false ) } @@ -225,7 +218,7 @@ fun TimePickerNoDividerPreview() { @Composable fun TimePickerCustomColorsPreview() { TimePicker( - timeFormat = TimeFormat.HOUR_12, + state = rememberTimePickerState(timeFormat = TimeFormat.HOUR_12), textStyle = TextStyle(fontSize = 16.sp, color = Color.Gray), selectedTextStyle = TextStyle(fontSize = 22.sp, color = Color(0xFF6200EE)), dividerColor = Color(0xFF6200EE) @@ -236,7 +229,7 @@ fun TimePickerCustomColorsPreview() { @Composable fun TimePickerLargeTextPreview() { TimePicker( - timeFormat = TimeFormat.HOUR_24, + state = rememberTimePickerState(timeFormat = TimeFormat.HOUR_24), textStyle = TextStyle(fontSize = 20.sp), selectedTextStyle = TextStyle(fontSize = 28.sp), visibleItemsCount = 5 diff --git a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BackgroundStylePickerScreen.kt b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BackgroundStylePickerScreen.kt index 732dfcf..d241513 100644 --- a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BackgroundStylePickerScreen.kt +++ b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BackgroundStylePickerScreen.kt @@ -31,7 +31,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kez.picker.date.YearMonthPicker -import com.kez.picker.rememberPickerState +import com.kez.picker.rememberTimePickerState +import com.kez.picker.rememberYearMonthPickerState import com.kez.picker.sample.formatTime12 import com.kez.picker.sample.getMonthName import com.kez.picker.time.TimePicker @@ -49,19 +50,23 @@ import kotlinx.datetime.number internal fun BackgroundStylePickerScreen( onBackPressed: () -> Unit = {}, ) { - val yearState = rememberPickerState(currentDate.year) - val monthState = rememberPickerState(currentDate.month.number) - val hourState = - rememberPickerState(if (currentHour > 12) currentHour - 12 else if (currentHour == 0) 12 else currentHour) - val minuteState = rememberPickerState(currentMinute) - val periodState = rememberPickerState(if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM) + val yearMonthState = rememberYearMonthPickerState( + initialYear = currentDate.year, + initialMonth = currentDate.monthNumber + ) + val timeState = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute, + initialPeriod = if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM, + timeFormat = TimeFormat.HOUR_12 + ) - val selectedDateText = remember(yearState.selectedItem, monthState.selectedItem) { - "${yearState.selectedItem}λ…„ ${getMonthName(monthState.selectedItem)}" + val selectedDateText = remember(yearMonthState.selectedYear, yearMonthState.selectedMonth) { + "${yearMonthState.selectedYear}λ…„ ${getMonthName(yearMonthState.selectedMonth)}" } val selectedTimeText = - remember(hourState.selectedItem, minuteState.selectedItem, periodState.selectedItem) { - formatTime12(hourState.selectedItem, minuteState.selectedItem, periodState.selectedItem) + remember(timeState.selectedHour, timeState.selectedMinute, timeState.selectedPeriod) { + formatTime12(timeState.selectedHour, timeState.selectedMinute, timeState.selectedPeriod) } Scaffold( @@ -118,8 +123,7 @@ internal fun BackgroundStylePickerScreen( horizontalAlignment = Alignment.CenterHorizontally ) { YearMonthPicker( - yearPickerState = yearState, - monthPickerState = monthState, + state = yearMonthState, textStyle = TextStyle( fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) @@ -152,10 +156,7 @@ internal fun BackgroundStylePickerScreen( horizontalAlignment = Alignment.CenterHorizontally ) { TimePicker( - hourPickerState = hourState, - minutePickerState = minuteState, - periodPickerState = periodState, - timeFormat = TimeFormat.HOUR_12, + state = timeState, textStyle = TextStyle( fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) diff --git a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BottomSheetSampleScreen.kt b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BottomSheetSampleScreen.kt index 9a1e22c..037b2fd 100644 --- a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BottomSheetSampleScreen.kt +++ b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/BottomSheetSampleScreen.kt @@ -38,7 +38,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.navigation.NavController import com.kez.picker.date.YearMonthPicker -import com.kez.picker.rememberPickerState +import com.kez.picker.rememberTimePickerState +import com.kez.picker.rememberYearMonthPickerState import com.kez.picker.sample.formatTime12 import com.kez.picker.sample.getMonthName import com.kez.picker.time.TimePicker @@ -60,12 +61,16 @@ internal fun BottomSheetSampleScreen( onBackPressed: () -> Unit = {}, ) { // Date/time state management - val yearState = rememberPickerState(currentDate.year) - val monthState = rememberPickerState(currentDate.month.number) - val hourState = - rememberPickerState(if (currentHour > 12) currentHour - 12 else if (currentHour == 0) 12 else currentHour) - val minuteState = rememberPickerState(currentMinute) - val periodState = rememberPickerState(if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM) + val yearMonthState = rememberYearMonthPickerState( + initialYear = currentDate.year, + initialMonth = currentDate.monthNumber + ) + val timeState = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute, + initialPeriod = if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM, + timeFormat = TimeFormat.HOUR_12 + ) // Selected date/time text var selectedDateText by remember { @@ -74,9 +79,9 @@ internal fun BottomSheetSampleScreen( var selectedTimeText by remember { mutableStateOf( formatTime12( - hourState.selectedItem, - minuteState.selectedItem, - periodState.selectedItem + timeState.selectedHour, + timeState.selectedMinute, + timeState.selectedPeriod ) ) } @@ -240,8 +245,7 @@ internal fun BottomSheetSampleScreen( contentAlignment = Alignment.Center ) { YearMonthPicker( - yearPickerState = yearState, - monthPickerState = monthState, + state = yearMonthState, textStyle = TextStyle( fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) @@ -261,7 +265,7 @@ internal fun BottomSheetSampleScreen( onClick = { // Update selected date selectedDateText = - "${yearState.selectedItem}λ…„ ${getMonthName(monthState.selectedItem)}" + "${yearMonthState.selectedYear}λ…„ ${getMonthName(yearMonthState.selectedMonth)}" scope.launch { dateSheetState.hide() showDateBottomSheet = false @@ -305,10 +309,7 @@ internal fun BottomSheetSampleScreen( contentAlignment = Alignment.Center ) { TimePicker( - hourPickerState = hourState, - minutePickerState = minuteState, - periodPickerState = periodState, - timeFormat = TimeFormat.HOUR_12, + state = timeState, textStyle = TextStyle( fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) @@ -328,9 +329,9 @@ internal fun BottomSheetSampleScreen( onClick = { // Update selected time selectedTimeText = formatTime12( - hourState.selectedItem, - minuteState.selectedItem, - periodState.selectedItem + timeState.selectedHour, + timeState.selectedMinute, + timeState.selectedPeriod ) scope.launch { timeSheetState.hide() diff --git a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/DatePickerSampleScreen.kt b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/DatePickerSampleScreen.kt index 78756e2..5d79a23 100644 --- a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/DatePickerSampleScreen.kt +++ b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/DatePickerSampleScreen.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.kez.picker.date.YearMonthPicker -import com.kez.picker.rememberPickerState +import com.kez.picker.rememberYearMonthPickerState import com.kez.picker.sample.getMonthName import com.kez.picker.util.currentDate import compose.icons.FeatherIcons @@ -43,13 +43,15 @@ import kotlinx.datetime.number internal fun DatePickerSampleScreen( onBackPressed: () -> Unit = {}, ) { - val yearState = rememberPickerState(currentDate.year) - val monthState = rememberPickerState(currentDate.month.number) + val state = rememberYearMonthPickerState( + initialYear = currentDate.year, + initialMonth = currentDate.monthNumber + ) // Calculate selected date text val selectedDateText by remember { derivedStateOf { - "${yearState.selectedItem}λ…„ ${getMonthName(monthState.selectedItem)}" + "${state.selectedYear}λ…„ ${getMonthName(state.selectedMonth)}" } } @@ -105,8 +107,7 @@ internal fun DatePickerSampleScreen( YearMonthPicker( modifier = Modifier.padding(horizontal = 12.dp), - yearPickerState = yearState, - monthPickerState = monthState + state = state ) // TODO: Add day selection capability diff --git a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/IntegratedPickerScreen.kt b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/IntegratedPickerScreen.kt index ffdf091..dfda8f7 100644 --- a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/IntegratedPickerScreen.kt +++ b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/IntegratedPickerScreen.kt @@ -49,9 +49,9 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.navigation.compose.rememberNavController import com.kez.picker.date.YearMonthPicker -import com.kez.picker.rememberPickerState +import com.kez.picker.rememberTimePickerState +import com.kez.picker.rememberYearMonthPickerState import com.kez.picker.sample.formatTime12 import com.kez.picker.sample.getMonthName import com.kez.picker.time.TimePicker @@ -64,27 +64,32 @@ import compose.icons.FeatherIcons import compose.icons.feathericons.ArrowLeft import compose.icons.feathericons.Calendar import compose.icons.feathericons.Clock +import kotlinx.datetime.number @OptIn(ExperimentalMaterial3Api::class) @Composable internal fun IntegratedPickerScreen( onBackPressed: () -> Unit = {}, ) { - val yearState = rememberPickerState(currentDate.year) - val monthState = rememberPickerState(currentDate.monthNumber) - val hourState = - rememberPickerState(if (currentHour > 12) currentHour - 12 else if (currentHour == 0) 12 else currentHour) - val minuteState = rememberPickerState(currentMinute) - val periodState = rememberPickerState(if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM) + val yearMonthState = rememberYearMonthPickerState( + initialYear = currentDate.year, + initialMonth = currentDate.month.number + ) + val timeState = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute, + initialPeriod = if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM, + timeFormat = TimeFormat.HOUR_12 + ) var selectedTabIndex by remember { mutableIntStateOf(0) } - val selectedDateText = remember(yearState.selectedItem, monthState.selectedItem) { - "${yearState.selectedItem}λ…„ ${getMonthName(monthState.selectedItem)}" + val selectedDateText = remember(yearMonthState.selectedYear, yearMonthState.selectedMonth) { + "${yearMonthState.selectedYear}λ…„ ${getMonthName(yearMonthState.selectedMonth)}" } val selectedTimeText = - remember(hourState.selectedItem, minuteState.selectedItem, periodState.selectedItem) { - formatTime12(hourState.selectedItem, minuteState.selectedItem, periodState.selectedItem) + remember(timeState.selectedHour, timeState.selectedMinute, timeState.selectedPeriod) { + formatTime12(timeState.selectedHour, timeState.selectedMinute, timeState.selectedPeriod) } Scaffold( @@ -162,8 +167,7 @@ internal fun IntegratedPickerScreen( ) { if (tabIndex == 0) { YearMonthPicker( - yearPickerState = yearState, - monthPickerState = monthState, + state = yearMonthState, textStyle = TextStyle( fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) @@ -177,10 +181,7 @@ internal fun IntegratedPickerScreen( ) } else { TimePicker( - hourPickerState = hourState, - minutePickerState = minuteState, - periodPickerState = periodState, - timeFormat = TimeFormat.HOUR_12, + state = timeState, textStyle = TextStyle( fontSize = 18.sp, color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f) diff --git a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/TimePickerSampleScreen.kt b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/TimePickerSampleScreen.kt index c505b0b..f0df747 100644 --- a/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/TimePickerSampleScreen.kt +++ b/sample/src/commonMain/kotlin/com/kez/picker/sample/ui/screen/TimePickerSampleScreen.kt @@ -34,7 +34,7 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import com.kez.picker.rememberPickerState +import com.kez.picker.rememberTimePickerState import com.kez.picker.time.TimePicker import com.kez.picker.util.TimeFormat import com.kez.picker.util.TimePeriod @@ -54,11 +54,16 @@ internal fun TimePickerSampleScreen( onBackPressed: () -> Unit = {}, ) { var selectedFormat by remember { mutableIntStateOf(0) } - val hour12State = - rememberPickerState(if (currentHour > 12) currentHour - 12 else if (currentHour == 0) 12 else currentHour) - val hour24State = rememberPickerState(currentHour) - val minuteState = rememberPickerState(currentMinute) - val periodState = rememberPickerState(if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM) + val timeState12 = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute, + initialPeriod = if (currentHour >= 12) TimePeriod.PM else TimePeriod.AM, + timeFormat = TimeFormat.HOUR_12 + ) + val timeState24 = rememberTimePickerState( + initialHour = currentHour, + initialMinute = currentMinute + ) val ktxTimeFormat12 = LocalTime.Format { amPmHour(padding = Padding.ZERO) @@ -77,9 +82,9 @@ internal fun TimePickerSampleScreen( val selectedTimeText by remember { derivedStateOf { if (selectedFormat == 0) { - val hour12 = hour12State.selectedItem // (1 ~ 12) - val minute = minuteState.selectedItem - val isAm = periodState.selectedItem == TimePeriod.AM + val hour12 = timeState12.selectedHour // (1 ~ 12) + val minute = timeState12.selectedMinute + val isAm = timeState12.selectedPeriod == TimePeriod.AM val hour24 = when { isAm && hour12 == 12 -> 0 // 12:xx AM (midnight) -> 0 @@ -90,7 +95,7 @@ internal fun TimePickerSampleScreen( time.format(ktxTimeFormat12) } else { - val time = LocalTime(hour24State.selectedItem, minuteState.selectedItem) + val time = LocalTime(timeState24.selectedHour, timeState24.selectedMinute) time.format(ktxTimeFormat24) } } @@ -159,17 +164,12 @@ internal fun TimePickerSampleScreen( Spacer(modifier = Modifier.height(32.dp)) if (selectedFormat == 0) { TimePicker( - hourPickerState = hour12State, - minutePickerState = minuteState, - periodPickerState = periodState, - timeFormat = TimeFormat.HOUR_12 + state = timeState12 ) } else { TimePicker( - hourPickerState = hour24State, - minutePickerState = minuteState, - visibleItemsCount = 5, - timeFormat = TimeFormat.HOUR_24 + state = timeState24, + visibleItemsCount = 5 ) } }