Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion nicegui/elements/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __init__(self,
*,
mask: str = 'YYYY-MM-DD',
on_change: Optional[Handler[ValueChangeEventArguments]] = None) -> None:
"""Date Input
"""Date Picker

This element is based on Quasar's `QDate <https://quasar.dev/vue-components/date>`_ component.
The date is a string in the format defined by the `mask` parameter.
Expand Down
63 changes: 63 additions & 0 deletions nicegui/elements/date_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from typing import Optional

from ..events import Handler, ValueChangeEventArguments
from .button import Button as button
from .date import Date as date
from .menu import Menu as menu
from .mixins.disableable_element import DisableableElement
from .mixins.label_element import LabelElement
from .mixins.value_element import ValueElement


class DateInput(LabelElement, ValueElement, DisableableElement):
LOOPBACK = False

def __init__(self,
label: Optional[str] = None, *,
range_input: bool = False,
placeholder: Optional[str] = None,
value: str = '',
on_change: Optional[Handler[ValueChangeEventArguments]] = None,
) -> None:
"""Date Input

This element extends Quasar's `QInput <https://quasar.dev/vue-components/input>`_ component with a date picker.

:param label: displayed label for the date input
:param range_input: if True, allows selecting a range of dates (value will be a dictionary with 'from' and 'to' keys)
:param placeholder: text to show if no date is selected
:param value: the current date value
:param on_change: callback to execute when the value changes
"""
self.range_input = range_input
super().__init__(tag='q-input', label=label, value=value, on_value_change=on_change)
if placeholder is not None:
self._props['placeholder'] = placeholder

with self.add_slot('append'):
with button(on_click=self._sync_value, icon='edit_calendar').props('flat round', remove='color').classes('cursor-pointer') as self.button:
with menu() as self.menu:
self.picker = date(on_change=lambda e: self.set_value(self._dict_to_string(e.value) if self.range_input else e.value)) \
.props('no-parent-event', remove='color')
if self.range_input:
self.picker.props('range')

def _dict_to_string(self, value: dict) -> str:
"""Convert a dictionary with 'from' and 'to' keys to a string."""
if not isinstance(value, dict) or 'from' not in value or 'to' not in value:
return ''
return f"{value['from']} - {value['to']}" if value else ''

def _string_to_dict(self, value: str) -> dict:
"""Convert a string formatted as 'start_date - end_date' to a dictionary."""
if ' - ' in value:
from_date, to_date = value.split(' - ')
return {'from': from_date, 'to': to_date}
return {}

def _sync_value(self) -> None:
"""Synchronize the value with the picker."""
if self.range_input and self.value:
self.picker.set_value(self._string_to_dict(self.value))
else:
self.picker.set_value(self.value or '')
2 changes: 1 addition & 1 deletion nicegui/elements/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self,
mask: str = 'HH:mm',
on_change: Optional[Handler[ValueChangeEventArguments]] = None,
) -> None:
"""Time Input
"""Time Picker

This element is based on Quasar's `QTime <https://quasar.dev/vue-components/time>`_ component.
The time is a string in the format defined by the `mask` parameter.
Expand Down
44 changes: 44 additions & 0 deletions nicegui/elements/time_input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import Optional

from ..events import Handler, ValueChangeEventArguments
from .button import Button as button
from .menu import Menu as menu
from .mixins.disableable_element import DisableableElement
from .mixins.label_element import LabelElement
from .mixins.value_element import ValueElement
from .time import Time as time


class TimeInput(LabelElement, ValueElement, DisableableElement):
LOOPBACK = False

def __init__(self,
label: Optional[str] = None, *,
placeholder: Optional[str] = None,
value: str = '',
on_change: Optional[Handler[ValueChangeEventArguments]] = None,
) -> None:
"""Time Input

This element extends Quasar's `QInput <https://quasar.dev/vue-components/input>`_ component with a time picker.

:param label: displayed label for the time input
:param placeholder: text to show if no time is selected
:param value: the current time value
:param on_change: callback to execute when the value changes
"""
super().__init__(tag='q-input', label=label, value=value, on_value_change=on_change)
if placeholder is not None:
self._props['placeholder'] = placeholder

with self.add_slot('append'):
with button(on_click=self._sync_value, icon='schedule').props('flat round', remove='color').classes('cursor-pointer') as self.button:
with menu() as self.menu:
self.picker = time(on_change=lambda e: self.set_value(e.value))

def _sync_value(self) -> None:
"""Synchronize the value with the picker."""
if self.value:
self.picker.set_value(self.value)
else:
self.picker.set_value('')
4 changes: 4 additions & 0 deletions nicegui/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'context_menu',
'dark_mode',
'date',
'date_input',
'dialog',
'download',
'drawer',
Expand Down Expand Up @@ -116,6 +117,7 @@
'teleport',
'textarea',
'time',
'time_input',
'timeline',
'timeline_entry',
'timer',
Expand Down Expand Up @@ -153,6 +155,7 @@
from .elements.context_menu import ContextMenu as context_menu
from .elements.dark_mode import DarkMode as dark_mode
from .elements.date import Date as date
from .elements.date_input import DateInput as date_input
from .elements.dialog import Dialog as dialog
from .elements.echart import EChart as echart
from .elements.editor import Editor as editor
Expand Down Expand Up @@ -220,6 +223,7 @@
from .elements.teleport import Teleport as teleport
from .elements.textarea import Textarea as textarea
from .elements.time import Time as time
from .elements.time_input import TimeInput as time_input
from .elements.timeline import Timeline as timeline
from .elements.timeline import TimelineEntry as timeline_entry
from .elements.timer import Timer as timer
Expand Down
2 changes: 2 additions & 0 deletions website/documentation/content/date_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def main_demo() -> None:


@doc.demo('Input element with date picker', '''
**Check out the [new implementation using `ui.date_input`](date_input)**. It is more convenient to use, and the picker button is implemented with a `ui.button` element.

This demo shows how to implement a date picker with an input element.
We place an icon in the input element's append slot.
When the icon is clicked, we open a menu with a date picker.
Expand Down
36 changes: 36 additions & 0 deletions website/documentation/content/date_input_documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from nicegui import ui

from . import doc


@doc.demo(ui.date_input)
def main_demo() -> None:
label = ui.label('date: 2025-05-31')
ui.date_input(label='Date', value='2025-05-31',
on_change=lambda e: label.set_text(f'date: {e.value}'))


@doc.demo('Date Range Input', '''
This demo shows how to use the date input with a range selection.

To respect the input which expects a string, the value is a string formatted as 'start_date - end_date'.
''')
def date_range_input_demo():
label = ui.label('date range: 2025-05-01 - 2025-05-31')
ui.date_input('Date range', value='2025-05-01 - 2025-05-31', range_input=True,
on_change=lambda e: label.set_text(f'date range: {e.value}'))


@doc.demo('Date Input with Date Filter', '''
This demo shows how to use the date input with a date filter by customizing `.picker`, the underlying `ui.date` element.

Read more about the date filter in the [`ui.date` documentation](date).
''')
def date_input_with_filter_demo():
label = ui.label('date: 2023-01-01')
date_input = ui.date_input(label='Date', value='2023-01-01',
on_change=lambda e: label.set_text(f'date: {e.value}'))
date_input.picker.props('''default-year-month=2023/01 :options="date => date <= '2023/01/15'"''')


doc.reference(ui.date_input)
4 changes: 4 additions & 0 deletions website/documentation/content/section_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
color_input_documentation,
color_picker_documentation,
date_documentation,
date_input_documentation,
doc,
input_documentation,
joystick_documentation,
Expand All @@ -22,6 +23,7 @@
switch_documentation,
textarea_documentation,
time_documentation,
time_input_documentation,
toggle_documentation,
upload_documentation,
)
Expand Down Expand Up @@ -49,6 +51,8 @@
doc.intro(knob_documentation)
doc.intro(color_input_documentation)
doc.intro(color_picker_documentation)
doc.intro(date_input_documentation)
doc.intro(date_documentation)
doc.intro(time_input_documentation)
doc.intro(time_documentation)
doc.intro(upload_documentation)
2 changes: 2 additions & 0 deletions website/documentation/content/time_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ def main_demo() -> None:


@doc.demo('Input element with time picker', '''
**Check out the [new implementation using `ui.time_input`](time_input)**. It is more convenient to use, and the picker button is implemented with a `ui.button` element.

This demo shows how to implement a time picker with an input element.
We place an icon in the input element's append slot.
When the icon is clicked, we open a menu with a time picker.
Expand Down
13 changes: 13 additions & 0 deletions website/documentation/content/time_input_documentation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from nicegui import ui

from . import doc


@doc.demo(ui.time_input)
def main_demo() -> None:
label = ui.label('time: 12:30')
ui.time_input(label='Time', value='12:30',
on_change=lambda e: label.set_text(f'time: {e.value}'))


doc.reference(ui.time_input)