Skip to content

Commit

Permalink
Improve state handling
Browse files Browse the repository at this point in the history
  • Loading branch information
maxkeppeler committed Sep 17, 2022
1 parent c7c3d8e commit 4bfc6e1
Show file tree
Hide file tree
Showing 43 changed files with 727 additions and 399 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,37 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.window.DialogProperties
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.SheetState
import com.maxkeppeker.sheets.core.views.base.DialogBase
import com.maxkeppeler.sheets.calendar.models.CalendarConfig
import com.maxkeppeler.sheets.calendar.models.CalendarSelection

/**
* Calendar dialog for the use-case to select a date or period in a typical calendar-view.
* @param show If the dialog should be displayed or not.
* @param state The state of the sheet.
* @param config The general configuration for the dialog.
* @param header The header to be displayed at the top of the dialog.
* @param onClose Listener that is invoked to indicate that the use-case is done and the view should be closed.
* @param properties DialogProperties for further customization of this dialog's behavior.
*/
@ExperimentalMaterial3Api
@Composable
fun CalendarDialog(
show: Boolean,
state: SheetState,
selection: CalendarSelection,
config: CalendarConfig = CalendarConfig(),
header: Header? = null,
onClose: () -> Unit,
properties: DialogProperties = DialogProperties(),
) {

DialogBase(
show = show,
onClose = onClose,
state = state,
properties = properties,
) {
CalendarView(
sheetState = state,
config = config,
header = header,
selection = selection,
onCancel = onClose
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ package com.maxkeppeler.sheets.calendar

import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.Saver
import com.maxkeppeker.sheets.core.views.BaseState
import androidx.compose.runtime.saveable.rememberSaveable
import com.maxkeppeker.sheets.core.views.BaseTypeState
import com.maxkeppeler.sheets.calendar.models.*
import com.maxkeppeler.sheets.calendar.utils.*
import java.io.Serializable
Expand All @@ -34,8 +35,9 @@ import java.time.Month
internal class CalendarState(
val selection: CalendarSelection,
val config: CalendarConfig,
stateData: CalendarStateData? = null
): BaseState() {
stateData: CalendarStateData? = null,
) : BaseTypeState() {

val today by mutableStateOf(LocalDate.now())
var mode by mutableStateOf(stateData?.mode ?: CalendarDisplayMode.CALENDAR)
var cameraDate by mutableStateOf(stateData?.cameraDate ?: selection.initialCameraDate)
Expand Down Expand Up @@ -193,8 +195,13 @@ internal class CalendarState(
}
}

companion object {
override fun reset() {
date.value = null
dates.clear()
range.clear()
}

companion object {
/**
* [Saver] implementation.
* @param selection The selection configuration for the dialog view.
Expand All @@ -214,7 +221,9 @@ internal class CalendarState(
rangeSelectionStart = state.isRangeSelectionStart
)
},
restore = { data -> CalendarState(selection, config, data) }
restore = { data ->
CalendarState(selection, config, data)
}
)
}

Expand Down Expand Up @@ -258,4 +267,18 @@ internal class CalendarState(
return result
}
}
}
}

/**
* Create a CalendarState and remember it.
* @param selection The selection configuration for the dialog view.
* @param config The general configuration for the dialog view.
*/
@Composable
internal fun rememberCalendarState(
selection: CalendarSelection,
config: CalendarConfig,
): CalendarState = rememberSaveable(
saver = CalendarState.Saver(selection, config),
init = { CalendarState(selection, config) }
)
106 changes: 56 additions & 50 deletions calendar/src/main/java/com/maxkeppeler/sheets/calendar/CalendarView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,58 +15,61 @@
*/
package com.maxkeppeler.sheets.calendar

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import com.maxkeppeker.sheets.core.models.base.BaseBehaviors
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.SheetState
import com.maxkeppeker.sheets.core.models.base.StateHandler
import com.maxkeppeker.sheets.core.views.ButtonsComponent
import com.maxkeppeker.sheets.core.views.HeaderComponent
import com.maxkeppeker.sheets.core.views.base.FrameBase
import com.maxkeppeler.sheets.calendar.models.*
import com.maxkeppeler.sheets.calendar.utils.*
import com.maxkeppeler.sheets.calendar.models.CalendarConfig
import com.maxkeppeler.sheets.calendar.models.CalendarDisplayMode
import com.maxkeppeler.sheets.calendar.models.CalendarSelection
import com.maxkeppeler.sheets.calendar.utils.endValue
import com.maxkeppeler.sheets.calendar.utils.startValue
import com.maxkeppeler.sheets.calendar.views.*
import java.time.LocalDate

/**
* Calendar dialog for the use-case to select a date or period in a typical calendar-view.
* @param sheetState The state of the sheet.
* @param selection The selection configuration for the dialog view.
* @param config The general configuration for the dialog view.
* @param header The header to be displayed at the top of the dialog view.
* @param onCancel Listener that is invoked when the use-case was canceled.
*/
@ExperimentalMaterial3Api
@Composable
fun CalendarView(
sheetState: SheetState,
selection: CalendarSelection,
config: CalendarConfig = CalendarConfig(),
header: Header? = null,
onCancel: () -> Unit = {},
) {
val calendarState = rememberCalendarState(selection, config)
StateHandler(sheetState, calendarState)

val state = rememberSaveable(
saver = CalendarState.Saver(selection, config),
init = { CalendarState(selection, config) }
)
val coroutine = rememberCoroutineScope()
val onSelection: (LocalDate) -> Unit = {
state.processSelection(it)
calendarState.processSelection(it)
BaseBehaviors.autoFinish(
selection = selection,
coroutine = coroutine,
onSelection = state::onFinish,
onFinished = onCancel,
onDisableInput = state::disableInput
onSelection = calendarState::onFinish,
onFinished = sheetState::finish,
onDisableInput = calendarState::disableInput
)
}

val yearListState = rememberLazyListState()
LaunchedEffect(state.mode) {
if (state.mode == CalendarDisplayMode.YEAR) {
yearListState.scrollToItem(state.yearIndex)
LaunchedEffect(calendarState.mode) {
if (calendarState.mode == CalendarDisplayMode.YEAR) {
yearListState.scrollToItem(calendarState.yearIndex)
}
}

Expand All @@ -75,62 +78,65 @@ fun CalendarView(
content = {
CalendarTopComponent(
config = config,
mode = state.mode,
navigationDisabled = state.monthRange == null || state.mode != CalendarDisplayMode.CALENDAR,
prevDisabled = state.isPrevDisabled,
nextDisabled = state.isNextDisabled,
cameraDate = state.cameraDate,
onPrev = state::onPrevious,
onNext = state::onNext,
onMonthClick = { state.onMonthSelectionClick() },
onYearClick = { state.onYearSelectionClick() },
mode = calendarState.mode,
navigationDisabled = calendarState.monthRange == null || calendarState.mode != CalendarDisplayMode.CALENDAR,
prevDisabled = calendarState.isPrevDisabled,
nextDisabled = calendarState.isNextDisabled,
cameraDate = calendarState.cameraDate,
onPrev = calendarState::onPrevious,
onNext = calendarState::onNext,
onMonthClick = { calendarState.onMonthSelectionClick() },
onYearClick = { calendarState.onYearSelectionClick() },
)
CalendarBaseSelectionComponent(
modifier = Modifier.wrapContentHeight(),
yearListState = yearListState,
mode = state.mode,
cells = state.cells,
nextDisabled = state.isNextDisabled,
prevDisabled = state.isPrevDisabled,
onNext = state::onNext,
onPrev = state::onPrevious,
mode = calendarState.mode,
cells = calendarState.cells,
nextDisabled = calendarState.isNextDisabled,
prevDisabled = calendarState.isPrevDisabled,
onNext = calendarState::onNext,
onPrev = calendarState::onPrevious,
onCalendarView = {
setupCalendarSelectionView(
config = config,
cells = state.cells,
data = state.calendarData,
today = state.today,
cells = calendarState.cells,
data = calendarState.calendarData,
today = calendarState.today,
selection = selection,
onSelect = onSelection,
selectedDate = state.date.value,
selectedDates = state.dates,
selectedRange = Pair(state.range.startValue, state.range.endValue),
selectedDate = calendarState.date.value,
selectedDates = calendarState.dates,
selectedRange = Pair(
calendarState.range.startValue,
calendarState.range.endValue
),
)
},
onMonthView = {
setupMonthSelectionView(
monthRange = state.monthRange!!,
selectedMonth = state.cameraDate.month,
onMonthClick = state::onMonthClick
monthRange = calendarState.monthRange!!,
selectedMonth = calendarState.cameraDate.month,
onMonthClick = calendarState::onMonthClick
)
},
onYearView = {
setupYearSelectionView(
yearsRange = state.yearsRange,
selectedYear = state.cameraDate.year,
onYearClick = state::onYearClick
yearsRange = calendarState.yearsRange,
selectedYear = calendarState.cameraDate.year,
onYearClick = calendarState::onYearClick
)
}
)
},
buttonsVisible = selection.withButtonView && state.mode == CalendarDisplayMode.CALENDAR
buttonsVisible = selection.withButtonView && calendarState.mode == CalendarDisplayMode.CALENDAR
) {
ButtonsComponent(
selection = selection,
onPositiveValid = state.valid,
onPositiveValid = calendarState.valid,
onNegative = { selection.onNegativeClick?.invoke() },
onPositive = state::onFinish,
onCancel = onCancel
onPositive = calendarState::onFinish,
onClose = sheetState::finish
)
}
}
12 changes: 5 additions & 7 deletions clock/src/main/java/com/maxkeppeler/sheets/clock/ClockDialog.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ package com.maxkeppeler.sheets.clock

import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.window.DialogProperties
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.SheetState
import com.maxkeppeker.sheets.core.views.base.DialogBase
import com.maxkeppeler.sheets.clock.models.ClockConfig
import com.maxkeppeler.sheets.clock.models.ClockSelection

/**
* Clock dialog for the use-case to to select a clock time.
* @param show If the dialog should be displayed or not.
* @param state The state of the sheet.
* @param selection The selection configuration for the dialog.
* @param config The general configuration for the dialog.
* @param header The header to be displayed at the top of the dialog.
Expand All @@ -37,24 +37,22 @@ import com.maxkeppeler.sheets.clock.models.ClockSelection
@ExperimentalMaterial3Api
@Composable
fun ClockDialog(
show: Boolean,
state: SheetState,
selection: ClockSelection,
config: ClockConfig = ClockConfig(),
header: Header? = null,
onClose: () -> Unit,
properties: DialogProperties = DialogProperties(),
) {

DialogBase(
show = show,
onClose = onClose,
state = state,
properties = properties,
) {
ClockView(
sheetState = state,
selection = selection,
config = config,
header = header,
onCancel = onClose
)
}
}
Loading

0 comments on commit 4bfc6e1

Please sign in to comment.