Pantrix UI

Time Picker

A time of day to choose from columns: the hours, the minutes, and by a clock of twelve hours the half of the day, each a column of five rows that scrolls through the rest, the chosen value filled in the middle of it.

import com.pantrixui.components.date.timepicker.PantrixTimePicker

Examples

An appointment on the quarter hour in a popover

A button shows the chosen time and opens the picker under it in a popover. The minutes are a quarter of an hour apart; the popover stays open while the hour and the minute are chosen, and closes on a press outside it.

var at by remember { mutableStateOf<PantrixTime?>(null) }
var open by remember { mutableStateOf(false) }
val strings = PantrixTheme.strings
PantrixPopover(
    visible = open,
    onDismissRequest = { open = false },
    anchor = {
        PantrixButton(
            text = at?.let { strings.clock(it.hour, it.minute) } ?: "Choose the appointment time",
            onClick = { open = !open },
            variant = ButtonVariant.Outline,
            leadingIcon = { PantrixIcon(PantrixIcons.Clock, contentDescription = null) },
            tagKey = "at"
        )
    },
    alignment = PopoverAlignment.Start,
    tagKey = "at"
) {
    PantrixTimePicker(
        selected = at,
        onSelect = { at = it },
        minuteStep = QUARTER,
        tagKey = "at"
    )
}

private const val QUARTER = 15

Working hours on the page

A time within working hours from a picker on the page: from 09:00 to 17:30, half an hour apart. The times outside them are faint; the chosen time is written under it.

var call by remember { mutableStateOf<PantrixTime?>(PantrixTime(OPENS, 0)) }
val strings = PantrixTheme.strings
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixTimePicker(
        selected = call,
        onSelect = { call = it },
        minTime = PantrixTime(OPENS, 0),
        maxTime = PantrixTime(LAST_CALL, HALF_HOUR),
        minuteStep = HALF_HOUR,
        tagKey = "call"
    )
    PantrixText(
        text = call?.let { "Call at: ${strings.clock(it.hour, it.minute)}" } ?: "Choose a time for the call",
        style = PantrixTheme.typography.body
    )
}

private const val HALF_HOUR = 30

private const val OPENS = 9

private const val LAST_CALL = 17

A day and a time together in a dialog

A meeting's day and time in a dialog. Where there is room the time stands to the right of the calendar; in a window too narrow for the two, as on a phone, it goes under the calendar (FlowRow). What the calendar and the picker show is a draft: OK turns the two into a moment on the device's clock with PantrixDate.at; the moment is written in UTC too.

var meeting by remember { mutableStateOf<Instant?>(null) }
var day by remember { mutableStateOf<PantrixDate?>(null) }
var time by remember { mutableStateOf<PantrixTime?>(null) }
var open by remember { mutableStateOf(false) }
val today = rememberToday()
val strings = PantrixTheme.strings
val report = LocalExampleEvents.current
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixButton(
        text = "Choose the meeting time",
        onClick = { open = true },
        variant = ButtonVariant.Outline,
        tagKey = "meeting"
    )
    meeting?.let { moment ->
        val shown = PantrixDate.of(moment)
        val clock = PantrixTime.of(moment)
        PantrixText(
            text = "Meeting: ${strings.date(shown.day, shown.month, shown.year)} " +
                strings.clock(clock.hour, clock.minute),
            style = PantrixTheme.typography.body
        )
        PantrixText(text = "UTC: $moment", style = PantrixTheme.typography.footnote)
    }
}
PantrixDialog(
    visible = open,
    onDismissRequest = { open = false },
    title = "Meeting time",
    actions = {
        PantrixButton(text = "Cancel", onClick = { open = false }, variant = ButtonVariant.Text, tagKey = "cancel")
        PantrixButton(
            text = "OK",
            onClick = {
                val chosenDay = day
                val chosenTime = time
                if (chosenDay != null && chosenTime != null) meeting = chosenDay.at(chosenTime)
                open = false
                report("Meeting time chosen")
            },
            enabled = day != null && time != null,
            tagKey = "accept"
        )
    },
    tagKey = "meeting"
) {
    FlowRow(
        horizontalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.md, Alignment.CenterHorizontally),
        verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)
    ) {
        PantrixDatePicker(selected = day, onSelect = { day = it }, minDate = today, tagKey = "meeting")
        PantrixTimePicker(
            selected = time,
            onSelect = { time = it },
            minuteStep = FIVE_MINUTES,
            tagKey = "meeting"
        )
    }
}

private const val FIVE_MINUTES = 5

12 or 24 hours

A reminder's time by the clock chosen. is24Hour gives the picker its shape: true two columns (00 to 23, 22:00), false the hours from 12 to 11 and a column of the half of the day (10:00 PM). The time is still a PantrixTime of 24 hours; only how it is shown changes.

var reminder by remember { mutableStateOf<PantrixTime?>(PantrixTime(REMINDER_HOUR, 0)) }
var is24Hour by remember { mutableStateOf(true) }
val strings = PantrixTheme.strings
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixSegmentButton(
        options = listOf(
            SegmentOption(true, "24 hours", tagKey = "twenty_four"),
            SegmentOption(false, "12 hours", tagKey = "twelve")
        ),
        selected = is24Hour,
        onSelect = { is24Hour = it },
        fullWidth = false,
        tagKey = "cycle"
    )
    PantrixTimePicker(
        selected = reminder,
        onSelect = { reminder = it },
        is24Hour = is24Hour,
        tagKey = "reminder"
    )
    PantrixText(
        text = reminder?.let { "Reminder: ${strings.time(it.hour, it.minute, is24Hour = is24Hour)} ($it)" }
            ?: "Choose a time for the reminder",
        style = PantrixTheme.typography.body
    )
}

private const val REMINDER_HOUR = 22

Hours past midnight

The start of a night shift: between 22:00 and 06:00, half an hour apart. As maxTime is before minTime, the hours that can be chosen pass midnight: 22 and 23, then 00 to 06; the hours between are faint. An hour after midnight falls on the next day, and the line under it says so.

val opens = PantrixTime(NIGHT_STARTS, 0)
var start by remember { mutableStateOf<PantrixTime?>(opens) }
val strings = PantrixTheme.strings
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixTimePicker(
        selected = start,
        onSelect = { start = it },
        minTime = opens,
        maxTime = PantrixTime(NIGHT_ENDS, 0),
        minuteStep = HALF_HOUR,
        tagKey = "night"
    )
    PantrixText(
        text = start?.let {
            val clock = strings.clock(it.hour, it.minute)
            if (it < opens) "Shift starts: $clock, the next day" else "Shift starts: $clock"
        } ?: "Choose when the shift starts",
        style = PantrixTheme.typography.body
    )
}

private const val HALF_HOUR = 30

private const val NIGHT_STARTS = 22

private const val NIGHT_ENDS = 6

Its own colours and disabled

The picker's own colours and measure, and a switch that disables it. The chosen time's fill and the columns' names are in the info colour; every colour not given is the theme's. The picker has no padding of its own (padding = 0.dp): it stands in the card's. With the switch off the picker is faint and takes no press, and the chosen time stays.

var alarm by remember { mutableStateOf<PantrixTime?>(PantrixTime(ALARM_HOUR, HALF_HOUR)) }
var enabled by remember { mutableStateOf(true) }
val colors = PantrixTheme.colors
Column(verticalArrangement = Arrangement.spacedBy(PantrixTheme.spacing.sm)) {
    PantrixSwitchButton(
        checked = enabled,
        onCheckedChange = { enabled = it },
        label = "Can be chosen",
        tagKey = "alarm_enabled"
    )
    PantrixTimePicker(
        selected = alarm,
        onSelect = { alarm = it },
        enabled = enabled,
        style = TimePickerStyle(padding = 0.dp),
        colors = TimePickerColors(selectedContainer = colors.info.color, heading = colors.info.color),
        tagKey = "alarm"
    )
}

private const val HALF_HOUR = 30

private const val ALARM_HOUR = 7

API

selected

A time of day to choose from columns: the hours, the minutes, and by a clock of twelve hours the half of the day, each a column of five rows that scrolls through the rest, the chosen value filled in the middle of it.

ParameterTypeDefaultWhat it is
selectedPantrixTime?the chosen time, or null while none is. Its seconds are kept in every time a press makes of it.
onSelect(PantrixTime) -> Unithanded the time a press or a key chose: one the picker takes.
modifierModifierModifierthe picker's modifier. The picker is as wide as its columns; given more room, it stands in the middle of it.
minTimePantrixTime?nullthe first time that can be chosen, or null for none so early: its hour and minute.
maxTimePantrixTime?nullthe last time that can be chosen, or null for none so late. Where it comes before minTime, the times that can be chosen run past midnight, from minTime to maxTime of the next day: a night's.
minuteStepInt1how far apart the minutes in their column are, 1 to 60: 15 offers the hour's first, a quarter past, half past and a quarter to. A chosen minute between them is shown all the same.
is24HourBooleantruewhether the hours are counted by twenty-four, and so the picker's shape: two columns that choose 22:00; or by twelve, with a third column of the half of the day, 10:00 PM. Twenty-four by default, in every language. Either way the time chosen is a PantrixTime of twenty-four hours; a screen writes it by the same clock with PantrixStrings.time(hour, minute, is24Hour = …).
enabledBooleantruewhether a time can be chosen at all. A picker that takes no choice is drawn faint.
styleTimePickerStyle?nullthis call's own style: over every enclosing one and under the density. Only the fields it sets change, on every platform.
colorsTimePickerColors?nullthis picker's own colours: the ones it sets, with the theme's for the rest.
tagKeyString?nullthe picker's automation key: it is TimePicker_<tagKey>, and its values are TimePicker_<tagKey>.Hour_14, .Minute_30 and .Period_pm.

TimePickerStyle

PantrixTimePicker's geometry: the room of a value in a column, the pill a chosen value is drawn in, how wide a column is, and the room around the columns.

ParameterTypeDefaultWhat it is
cellSizeDpDp.UnspecifiedThe height of a value's room: a row.
markHeightDpDp.UnspecifiedThe height of the pill a chosen value and the value under a pointer are drawn in, in the middle of its room. It is most of the column wide.
columnWidthDpDp.UnspecifiedThe width of a column of values.
paddingDpDp.UnspecifiedThe room between the picker's edge and its columns.
gapDpDp.UnspecifiedBetween the columns' names and their values.

TimePickerColors

What a PantrixTimePicker is painted with, in place of the theme's colours: a colour that is set is used, and one that is left out stays the theme's.

ParameterTypeDefaultWhat it is
contentColorColor.UnspecifiedThe values' numbers and the mark between the hours and the minutes. The theme's textPrimary.
headingColorColor.UnspecifiedThe columns' names over them. The theme's textSecondary.
selectedContainerColorColor.UnspecifiedThe pill of a chosen value. The primary role's color.
selectedContentColorColor.UnspecifiedThe number on it. The primary role's onColor, or the colour a role's fill given as selectedContainer gives what stands on it.
hoverColorColor.UnspecifiedThe pill under a pointer: which value a press would choose. A share of content.
disabledColorColor.UnspecifiedThe values that cannot be chosen, and everything of a picker that takes no choice. The theme's textDisabled.

Accessibility