Skip to content
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

docs: date picker spec #388

Merged
merged 6 commits into from
Apr 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions plugins/ui/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -1354,6 +1354,135 @@ list_view7 = ui.list_view(
)
```

###### ui.date_picker
A date picker that can be used to select a date.

There are three types that can be passed in to the props that control the date format:
1. `LocalDate`: A LocalDate is a date without a time zone in the ISO-8601 system, such as "2007-12-03" or "2057-01-28".
jnumainville marked this conversation as resolved.
Show resolved Hide resolved
This will create a date picker with a granularity of days.
2. `Instant`: An Instant represents an unambiguous specific point on the timeline, such as 2021-04-12T14:13:07 UTC.
This will create a date picker with a granularity of seconds in UTC.
3. `ZonedDateTime`: A ZonedDateTime represents an unambiguous specific point on the timeline with an associated time zone, such as 2021-04-12T14:13:07 America/New_York.
This will create a date picker with a granularity of seconds in the specified time zone.

The format of the date picker and the type of the value passed to the `on_change` handler
is determined by the type of the following props in order of precedence:
1. `value`
2. `default_value`
3. `placeholder_value`

If none of these are provided, the `on_change` handler will be passed an `Instant`.

```py
import deephaven.ui as ui
ui.date_picker(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am surprised that this doesn't support a parameter combination like ui.date_picker(table: Table, date_col: str). I would expect to be able to pass a table in along with a column name for a column containing one of the valid types, and have the picker automatically provide the allowable date ranges accordingly. Not sure what the right default would be, maybe the entire date range?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mofojed @dsmmcken thoughts?
I was thinking about a table source but thought it didn't make much sense due to differing resolutions. I suppose making the column an alternative to min_value or max_value is possible, but seems to be a lot of work for something that could be done through a combination of table operations and use_cell_data hooks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea @alexpeters1208 for that case, wouldn't a ui.picker make more sense instead when selecting a date from a table? As for a range, yea I think use_cell_data would work fine for that case... we should mock out an example though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://react-spectrum.adobe.com/react-spectrum/DateRangePicker.html is a different thing, but also maybe relevant to the discussion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mofojed My understanding is that date_picker gives you a kind of calendar pop-up, while picker is a drop-down list. I don't think I'd want to use a drop down list for dates, seems like that list gets really long really quick. Or maybe I'm not understanding?

Copy link
Collaborator Author

@jnumainville jnumainville Mar 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fairly contrived and simplified example but represents how you could derive a value from a table for a range already. It's not difficult to do the end range too but I wanted to show that there is lots of flexibility that might be desired that I think is best achieved outside of the date_picker itself.

table = time_table(period="PT2S")
start = table.head(1)

min_value = ui.use_cell_data(start)
max_value = "2024-29-03"

date_picker = ui.date_picker(min_value=min_value, max_value=max_value)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know you've excluded it, but the isDateUnavailable prop feels kinda like something that could be a column.

Copy link
Collaborator Author

@jnumainville jnumainville Mar 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the same thought but quickly ran into trouble when considering how exactly that would be implemented. It doesn't make much sense as a separate boolean column because you would have to check the boolean + date column.
This brings me to the possibility of having a table for unavailable_values with a date column (which I basically emulate with a list of dates at the moment) but is it worth implementing table backing for that?

placeholder_value: Date | None = None,
jnumainville marked this conversation as resolved.
Show resolved Hide resolved
value: Date | None = None,
default_value: Date | None = None,
min_value: Date | None = None,
max_value: Date | None = None,
unavailable_values: Sequence[Date] | None = None,
granularity: Granularity | None = None,
on_change: Callable[[Date], None] | None = None,
**props: Any
) -> ListViewElement
```

###### Parameters
| Parameter | Type | Description |
|----------------------|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `placeholder_value` | `Date \| None` | A placeholder date that influences the format of the placeholder shown when no value is selected. Defaults to today at midnight in the user's timezone. |
| `value` | `Date \| None` | The current value (controlled). |
| `default_value` | `Date \| None` | The default value (uncontrolled). |
| `min_value` | `Date \| None` | The minimum allowed date that a user may select. |
| `max_value` | `Date \| None` | The maximum allowed date that a user may select. |
| `unavailable_values` | `Sequence[Date] \| None` | A list of dates that cannot be selected. |
| `granularity` | `Granularity \| None` | Determines the smallest unit that is displayed in the date picker. By default, this is `"DAY"` for `LocalDate`, and `"SECOND"` otherwise. |
| `on_change` | `Callable[[Date], None] \| None` | Handler that is called when the value changes. The exact `Date` type will be the same as the type passed to `value`, `default_value` or `placeholder_value`, in that order of precedence. |
| `**props` | `Any` | Any other [DatePicker](https://react-spectrum.adobe.com/react-spectrum/DatePicker.html) prop, with the exception of `isDateUnavailable`, `validate`, and `errorMessage` (as a callback) |

```py

import deephaven.ui as ui
from deephaven.time import to_j_local_date, dh_today, to_j_instant, to_j_zdt

zoned_date_time = to_j_zdt("1995-03-22T11:11:11.23142 UTC")
jnumainville marked this conversation as resolved.
Show resolved Hide resolved
instant = to_j_instant("2022-01-01T00:00:00 ET")
local_date = to_j_local_date(dh_today())

# simple date picker that takes ui.items and is uncontrolled
# this creates a date picker with a granularity of days with a default value of today
date_picker1 = ui.date_picker(
default_value=local_date
)

# simple date picker that takes list view items directly and is controlled
# this creates a date picker with a granularity of seconds in UTC
# the on_change handler is passed an instant
date, set_date = ui.use_state(instant)

date_picker2 = ui.date_picker(
value=date,
on_change=set_date
)

# this creates a date picker with a granularity of seconds in the specified time zone
# the on_change handler is passed a zoned date time
date, set_date = ui.use_state(None)

date_picker3 = ui.date_picker(
placeholder_value=zoned_date_time,
on_change=set_date
)

# this creates a date picker with a granularity of seconds in UTC
# the on_change handler is passed an instant
date, set_date = ui.use_state(None)

date_picker4 = ui.date_picker(
placeholder_value=instant,
on_change=set_date
)

# this creates a date picker with a granularity of days
# the on_change handler is passed a local date
date, set_date = ui.use_state(None)

date_picker5 = ui.date_picker(
placeholder_value=local_date,
on_change=set_date
)

# this creates a date picker with a granularity of days, but the on_change handler is still passed an instant
date, set_date = ui.use_state(None)

date_picker6 = ui.date_picker(
placeholder_value=instant,
granularity="day",
on_change=set_date
)

# this creates a date picker with a granularity of seconds and the on_change handler is passed an instant
date, set_date = ui.use_state(None)

date_picker7 = ui.date_picker(
on_change=set_date
)

# this create a date picker with a granularity of days, a min and max value, and unavailable dates
min_value = to_j_local_date("2022-01-01")
max_value = to_j_local_date("2022-12-31")
unavailable_dates = [to_j_local_date("2022-03-15"), to_j_local_date("2022-03-17")]
date, set_date = ui.use_state(to_j_local_date("2022-03-16"))
date_picker8 = ui.date_picker(
value=date,
min_value=min_value,
max_value=max_value,
unavailable_values=unavailable_dates,
on_change=set_date
)
```

#### ui.table

`ui.table` is a wrapper for a Deephaven `Table` object that allows you to add UI customizations or callbacks. The basic syntax for creating a `UITable` is:
Expand Down Expand Up @@ -1873,6 +2002,11 @@ Key = Stringable
ActionKey = Key
Selection = Sequence[Key]
ListViewItem = Stringable | ItemElement
LocalDateConvertible = Union[None, LocalDate, str, datetime.date, datetime.datetime, numpy.datetime64, pandas.Timestamp]
InstantConvertible = Union[None, Instant, int, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
ZonedDateTimeConvertible = Union[None, ZonedDateTime, str, datetime.datetime, numpy.datetime64, pandas.Timestamp]
Date = Instant | LocalDate | ZonedDateTime | LocalDateConvertible | InstantConvertible | ZonedDateTimeConvertible
Granularity = Literal["DAY", "HOUR", "MINUTE", "SECOND"]

T = TypeVar("T")
Combination: TypeAlias = T | set[T] | Sequence[T]
Expand Down
Loading