mirror of
https://github.com/gradio-app/gradio.git
synced 2025-02-23 11:39:17 +08:00
gr.DateTime component (#8713)
* changes * changes * add changeset * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * Update gradio/components/datetime.py Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * changes * changes --------- Co-authored-by: Ali Abid <aliabid94@gmail.com> Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com> Co-authored-by: Abubakar Abid <abubakar@huggingface.co>
This commit is contained in:
parent
b736c8db34
commit
e3c7079e38
8
.changeset/sharp-ideas-run.md
Normal file
8
.changeset/sharp-ideas-run.md
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
"@gradio/app": minor
|
||||
"@gradio/icons": minor
|
||||
"@gradio/plot": minor
|
||||
"gradio": minor
|
||||
---
|
||||
|
||||
feat:Time range component
|
1
demo/datetime_component/run.ipynb
Normal file
1
demo/datetime_component/run.ipynb
Normal file
@ -0,0 +1 @@
|
||||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: datetime_component"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " gr.DateTime()\n", "\n", "demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
6
demo/datetime_component/run.py
Normal file
6
demo/datetime_component/run.py
Normal file
@ -0,0 +1,6 @@
|
||||
import gradio as gr
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
gr.DateTime()
|
||||
|
||||
demo.launch()
|
1
demo/datetimes/run.ipynb
Normal file
1
demo/datetimes/run.ipynb
Normal file
@ -0,0 +1 @@
|
||||
{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: datetimes"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " date1 = gr.DateTime(include_time=True, label=\"Date and Time\", type=\"datetime\", elem_id=\"date1\")\n", " date2 = gr.DateTime(include_time=False, label=\"Date Only\", type=\"string\", elem_id=\"date2\")\n", " date3 = gr.DateTime(elem_id=\"date3\", timezone=\"Europe/Paris\")\n", "\n", " with gr.Row():\n", " btn1 = gr.Button(\"Load Date 1\")\n", " btn2 = gr.Button(\"Load Date 2\")\n", " btn3 = gr.Button(\"Load Date 3\")\n", "\n", " click_output = gr.Textbox(label=\"Last Load\")\n", " change_output = gr.Textbox(label=\"Last Change\")\n", " submit_output = gr.Textbox(label=\"Last Submit\")\n", "\n", " btn1.click(lambda x:x, date1, click_output)\n", " btn2.click(lambda x:x, date2, click_output)\n", " btn3.click(lambda x:x, date3, click_output)\n", "\n", " for item in [date1, date2, date3]:\n", " item.change(lambda x:x, item, change_output)\n", " item.submit(lambda x:x, item, submit_output)\n", " \n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
27
demo/datetimes/run.py
Normal file
27
demo/datetimes/run.py
Normal file
@ -0,0 +1,27 @@
|
||||
import gradio as gr
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
date1 = gr.DateTime(include_time=True, label="Date and Time", type="datetime", elem_id="date1")
|
||||
date2 = gr.DateTime(include_time=False, label="Date Only", type="string", elem_id="date2")
|
||||
date3 = gr.DateTime(elem_id="date3", timezone="Europe/Paris")
|
||||
|
||||
with gr.Row():
|
||||
btn1 = gr.Button("Load Date 1")
|
||||
btn2 = gr.Button("Load Date 2")
|
||||
btn3 = gr.Button("Load Date 3")
|
||||
|
||||
click_output = gr.Textbox(label="Last Load")
|
||||
change_output = gr.Textbox(label="Last Change")
|
||||
submit_output = gr.Textbox(label="Last Submit")
|
||||
|
||||
btn1.click(lambda x:x, date1, click_output)
|
||||
btn2.click(lambda x:x, date2, click_output)
|
||||
btn3.click(lambda x:x, date3, click_output)
|
||||
|
||||
for item in [date1, date2, date3]:
|
||||
item.change(lambda x:x, item, change_output)
|
||||
item.submit(lambda x:x, item, submit_output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
@ -65,7 +65,6 @@ def line_plot_fn(dataset):
|
||||
tooltip=['country', 'life_expect'],
|
||||
overlay_point=False,
|
||||
title="Life expectancy for countries",
|
||||
stroke_dash_legend_title="Country Cluster",
|
||||
)
|
||||
|
||||
|
||||
|
@ -26,6 +26,7 @@ from gradio.components import (
|
||||
DataFrame,
|
||||
Dataframe,
|
||||
Dataset,
|
||||
DateTime,
|
||||
DownloadButton,
|
||||
Dropdown,
|
||||
DuplicateButton,
|
||||
|
@ -19,6 +19,7 @@ from gradio.components.code import Code
|
||||
from gradio.components.color_picker import ColorPicker
|
||||
from gradio.components.dataframe import Dataframe
|
||||
from gradio.components.dataset import Dataset
|
||||
from gradio.components.datetime import DateTime
|
||||
from gradio.components.download_button import DownloadButton
|
||||
from gradio.components.dropdown import Dropdown
|
||||
from gradio.components.duplicate_button import DuplicateButton
|
||||
@ -95,6 +96,7 @@ __all__ = [
|
||||
"Markdown",
|
||||
"MessageDict",
|
||||
"Textbox",
|
||||
"DateTime",
|
||||
"Dropdown",
|
||||
"Model3D",
|
||||
"File",
|
||||
|
155
gradio/components/datetime.py
Normal file
155
gradio/components/datetime.py
Normal file
@ -0,0 +1,155 @@
|
||||
"""gr.DateTime() component."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Literal
|
||||
|
||||
import pytz
|
||||
from gradio_client.documentation import document
|
||||
|
||||
from gradio.components.base import FormComponent
|
||||
from gradio.events import Events
|
||||
|
||||
|
||||
@document()
|
||||
class DateTime(FormComponent):
|
||||
"""
|
||||
Component to select a date and (optionally) a time.
|
||||
"""
|
||||
|
||||
EVENTS = [
|
||||
Events.change,
|
||||
Events.submit,
|
||||
]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: float | str | datetime | None = None,
|
||||
*,
|
||||
include_time: bool = True,
|
||||
type: Literal["timestamp", "datetime", "string"] = "timestamp",
|
||||
timezone: str | None = None,
|
||||
label: str | None = None,
|
||||
show_label: bool | None = None,
|
||||
info: str | None = None,
|
||||
every: float | None = None,
|
||||
scale: int | None = None,
|
||||
min_width: int = 160,
|
||||
visible: bool = True,
|
||||
elem_id: str | None = None,
|
||||
elem_classes: list[str] | str | None = None,
|
||||
render: bool = True,
|
||||
key: int | str | None = None,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
value: default value for datetime.
|
||||
label: The label for this component. Appears above the component and is also used as the header if there are a table of examples for this component. If None and used in a `gr.Interface`, the label will be the name of the parameter this component is assigned to.
|
||||
show_label: if True, will display label.
|
||||
include_time: If True, the component will include time selection. If False, only date selection will be available.
|
||||
type: The type of the value. Can be "timestamp", "datetime", or "string". If "timestamp", the value will be a number representing the start and end date in seconds since epoch. If "datetime", the value will be a datetime object. If "string", the value will be the date entered by the user.
|
||||
timezone: The timezone to use for timestamps, such as "US/Pacific" or "Europe/Paris". If None, the timezone will be the local timezone.
|
||||
info: additional component description.
|
||||
every: If `value` is a callable, run the function 'every' number of seconds while the client connection is open. Has no effect otherwise. The event can be accessed (e.g. to cancel it) via this component's .load_event attribute.
|
||||
scale: relative size compared to adjacent Components. For example if Components A and B are in a Row, and A has scale=2, and B has scale=1, A will be twice as wide as B. Should be an integer. scale applies in Rows, and to top-level Components in Blocks where fill_height=True.
|
||||
min_width: minimum pixel width, will wrap if not sufficient screen space to satisfy this value. If a certain scale value results in this Component being narrower than min_width, the min_width parameter will be respected first.
|
||||
visible: If False, component will be hidden.
|
||||
elem_classes: An optional list of strings that are assigned as the classes of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
render: If False, component will not render be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
|
||||
key: if assigned, will be used to assume identity across a re-render. Components that have the same key across a re-render will have their value preserved.
|
||||
"""
|
||||
super().__init__(
|
||||
every=every,
|
||||
scale=scale,
|
||||
min_width=min_width,
|
||||
visible=visible,
|
||||
label=label,
|
||||
show_label=show_label,
|
||||
info=info,
|
||||
elem_id=elem_id,
|
||||
elem_classes=elem_classes,
|
||||
render=render,
|
||||
key=key,
|
||||
value=value,
|
||||
)
|
||||
self.type = type
|
||||
self.include_time = include_time
|
||||
self.time_format = "%Y-%m-%d %H:%M:%S" if include_time else "%Y-%m-%d"
|
||||
self.timezone = timezone
|
||||
|
||||
def preprocess(self, payload: str | None) -> str | float | datetime | None:
|
||||
"""
|
||||
Parameters:
|
||||
payload: the text entered in the textarea.
|
||||
Returns:
|
||||
Passes text value as a {str} into the function.
|
||||
"""
|
||||
if payload is None or payload == "":
|
||||
return None
|
||||
if self.type == "string" and "now" not in payload:
|
||||
return payload
|
||||
datetime = self.get_datetime_from_str(payload)
|
||||
if self.type == "string":
|
||||
return datetime.strftime(self.time_format)
|
||||
if self.type == "datetime":
|
||||
return datetime
|
||||
elif self.type == "timestamp":
|
||||
return datetime.timestamp()
|
||||
|
||||
def postprocess(self, value: float | datetime | str | None) -> str | None:
|
||||
"""
|
||||
Parameters:
|
||||
value: Expects a tuple pair of datetimes.
|
||||
Returns:
|
||||
A tuple pair of timestamps.
|
||||
"""
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
if isinstance(value, datetime):
|
||||
return datetime.strftime(value, self.time_format)
|
||||
elif isinstance(value, str):
|
||||
return value
|
||||
else:
|
||||
return datetime.fromtimestamp(
|
||||
value, tz=pytz.timezone(self.timezone) if self.timezone else None
|
||||
).strftime(self.time_format)
|
||||
|
||||
def api_info(self) -> dict[str, Any]:
|
||||
return {
|
||||
"type": "string",
|
||||
"description": f"Formatted as YYYY-MM-DD{' HH:MM:SS' if self.include_time else ''}",
|
||||
}
|
||||
|
||||
def example_payload(self) -> str:
|
||||
return "2020-10-01 05:20:15"
|
||||
|
||||
def example_value(self) -> str:
|
||||
return "2020-10-01 05:20:15"
|
||||
|
||||
def get_datetime_from_str(self, date: str) -> datetime:
|
||||
now_regex = r"^(?:\s*now\s*(?:-\s*(\d+)\s*([dmhs]))?)?\s*$"
|
||||
|
||||
if "now" in date:
|
||||
match = re.match(now_regex, date)
|
||||
if match:
|
||||
num = int(match.group(1) or 0)
|
||||
unit = match.group(2) or "s"
|
||||
if unit == "d":
|
||||
delta = timedelta(days=num)
|
||||
elif unit == "h":
|
||||
delta = timedelta(hours=num)
|
||||
elif unit == "m":
|
||||
delta = timedelta(minutes=num)
|
||||
else:
|
||||
delta = timedelta(seconds=num)
|
||||
return datetime.now() - delta
|
||||
else:
|
||||
raise ValueError("Invalid 'now' time format")
|
||||
else:
|
||||
dt = datetime.strptime(date, self.time_format)
|
||||
if self.timezone:
|
||||
dt = pytz.timezone(self.timezone).localize(dt)
|
||||
return dt
|
@ -9,6 +9,7 @@ from gradio_client.documentation import document
|
||||
|
||||
from gradio.components.base import Component
|
||||
from gradio.components.plot import AltairPlot, AltairPlotData, Plot
|
||||
from gradio.events import Events
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import pandas as pd
|
||||
@ -27,6 +28,8 @@ class LinePlot(Plot):
|
||||
|
||||
data_model = AltairPlotData
|
||||
|
||||
EVENTS = [Events.select]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: pd.DataFrame | Callable | None = None,
|
||||
@ -73,7 +76,6 @@ class LinePlot(Plot):
|
||||
x_lim: list[int] | None = None,
|
||||
y_lim: list[int] | None = None,
|
||||
caption: str | None = None,
|
||||
interactive: bool | None = True,
|
||||
label: str | None = None,
|
||||
show_label: bool | None = None,
|
||||
container: bool = True,
|
||||
@ -87,17 +89,18 @@ class LinePlot(Plot):
|
||||
render: bool = True,
|
||||
key: int | str | None = None,
|
||||
show_actions_button: bool = False,
|
||||
interactive: bool | None = None,
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
value: The pandas dataframe containing the data to display in a scatter plot.
|
||||
x: Column corresponding to the x axis.
|
||||
y: Column corresponding to the y axis.
|
||||
x: Column corresponding to the x axis. Can be grouped if datetime, e.g. 'yearmonth(date)' or 'minuteseconds(date)' with a column name 'date'. Any time unit supported by altair can be used.
|
||||
y: Column corresponding to the y axis. Can be aggregated, e.g. 'sum(price)' or 'count(price)' with a column name 'price'. Any aggregation function supported by altair can be used.
|
||||
color: The column to determine the point color. If the column contains numeric data, gradio will interpolate the column data so that small values correspond to light colors and large values correspond to dark values.
|
||||
stroke_dash: The column to determine the symbol used to draw the line, e.g. dashed lines, dashed lines with points.
|
||||
overlay_point: Whether to draw a point on the line for each (x, y) coordinate pair.
|
||||
title: The title to display on top of the chart.
|
||||
tooltip: The column (or list of columns) to display on the tooltip when a user hovers a point on the plot.
|
||||
tooltip: The column (or list of columns) to display on the tooltip when a user hovers a point on the plot. Set to [] to disable tooltips.
|
||||
x_title: The title given to the x axis. By default, uses the value of the x parameter.
|
||||
y_title: The title given to the y axis. By default, uses the value of the y parameter.
|
||||
x_label_angle: The angle for the x axis labels. Positive values are clockwise, and negative values are counter-clockwise.
|
||||
@ -111,7 +114,7 @@ class LinePlot(Plot):
|
||||
x_lim: A tuple or list containing the limits for the x-axis, specified as [x_min, x_max].
|
||||
y_lim: A tuple of list containing the limits for the y-axis, specified as [y_min, y_max].
|
||||
caption: The (optional) caption to display below the plot.
|
||||
interactive: Whether users should be able to interact with the plot by panning or zooming with their mouse or trackpad.
|
||||
interactive: Deprecated.
|
||||
label: The (optional) label to display on the top left corner of the plot.
|
||||
show_label: Whether the label should be displayed.
|
||||
every: Continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
|
||||
@ -127,7 +130,9 @@ class LinePlot(Plot):
|
||||
self.y = y
|
||||
self.color = color
|
||||
self.stroke_dash = stroke_dash
|
||||
self.tooltip = tooltip
|
||||
self.tooltip = (
|
||||
tooltip if tooltip is not None else [elem for elem in [x, y, color] if elem]
|
||||
)
|
||||
self.title = title
|
||||
self.x_title = x_title
|
||||
self.y_title = y_title
|
||||
@ -141,7 +146,6 @@ class LinePlot(Plot):
|
||||
self.x_lim = x_lim
|
||||
self.y_lim = y_lim
|
||||
self.caption = caption
|
||||
self.interactive_chart = interactive
|
||||
if isinstance(width, str):
|
||||
width = None
|
||||
warnings.warn(
|
||||
@ -172,6 +176,11 @@ class LinePlot(Plot):
|
||||
every=every,
|
||||
inputs=inputs,
|
||||
)
|
||||
if interactive is not None:
|
||||
warnings.warn(
|
||||
"The `interactive` parameter is deprecated and will be removed in a future version. "
|
||||
"The LinePlot component is always interactive."
|
||||
)
|
||||
|
||||
def get_block_name(self) -> str:
|
||||
return "plot"
|
||||
@ -220,25 +229,23 @@ class LinePlot(Plot):
|
||||
width: int | None = None,
|
||||
x_lim: list[int] | None = None,
|
||||
y_lim: list[int] | None = None,
|
||||
interactive: bool | None = None,
|
||||
):
|
||||
"""Helper for creating the scatter plot."""
|
||||
import altair as alt
|
||||
|
||||
interactive = True if interactive is None else interactive
|
||||
encodings = {
|
||||
"x": alt.X(
|
||||
x, # type: ignore
|
||||
title=x_title or x, # type: ignore
|
||||
scale=AltairPlot.create_scale(x_lim), # type: ignore
|
||||
x,
|
||||
title=x_title or x,
|
||||
scale=AltairPlot.create_scale(x_lim),
|
||||
axis=alt.Axis(labelAngle=x_label_angle)
|
||||
if x_label_angle is not None
|
||||
else alt.Axis(),
|
||||
),
|
||||
"y": alt.Y(
|
||||
y, # type: ignore
|
||||
title=y_title or y, # type: ignore
|
||||
scale=AltairPlot.create_scale(y_lim), # type: ignore
|
||||
y,
|
||||
title=y_title or y,
|
||||
scale=AltairPlot.create_scale(y_lim),
|
||||
axis=alt.Axis(labelAngle=y_label_angle)
|
||||
if y_label_angle is not None
|
||||
else alt.Axis(),
|
||||
@ -265,46 +272,60 @@ class LinePlot(Plot):
|
||||
),
|
||||
}
|
||||
|
||||
highlight = None
|
||||
if interactive and any([color, stroke_dash]):
|
||||
highlight = alt.selection(
|
||||
type="single", # type: ignore
|
||||
on="mouseover",
|
||||
fields=[c for c in [color, stroke_dash] if c],
|
||||
nearest=True,
|
||||
)
|
||||
|
||||
if stroke_dash:
|
||||
stroke_dash = {
|
||||
"field": stroke_dash, # type: ignore
|
||||
"legend": AltairPlot.create_legend( # type: ignore
|
||||
position=stroke_dash_legend_position, # type: ignore
|
||||
title=stroke_dash_legend_title or stroke_dash, # type: ignore
|
||||
), # type: ignore
|
||||
} # type: ignore
|
||||
stroke_dash_encoding = {
|
||||
"field": stroke_dash,
|
||||
"legend": AltairPlot.create_legend(
|
||||
position=stroke_dash_legend_position or "bottom",
|
||||
title=stroke_dash_legend_title,
|
||||
),
|
||||
}
|
||||
else:
|
||||
stroke_dash = alt.value(alt.Undefined) # type: ignore
|
||||
stroke_dash_encoding = alt.value(alt.Undefined)
|
||||
|
||||
if tooltip:
|
||||
encodings["tooltip"] = tooltip
|
||||
|
||||
chart = alt.Chart(value).encode(**encodings) # type: ignore
|
||||
chart = alt.Chart(value).encode(**encodings)
|
||||
|
||||
points = chart.mark_point(clip=True).encode(
|
||||
opacity=alt.value(alt.Undefined) if overlay_point else alt.value(0),
|
||||
)
|
||||
lines = chart.mark_line(clip=True).encode(strokeDash=stroke_dash)
|
||||
lines = chart.mark_line(clip=True).encode(strokeDash=stroke_dash_encoding)
|
||||
|
||||
if highlight:
|
||||
points = points.add_selection(highlight)
|
||||
highlight = alt.selection_point(
|
||||
on="mouseover",
|
||||
fields=[c for c in [color, stroke_dash] if c],
|
||||
nearest=True,
|
||||
clear="mouseout",
|
||||
empty=False,
|
||||
)
|
||||
points = points.add_params(highlight)
|
||||
lines = lines.encode(
|
||||
size=alt.condition(highlight, alt.value(4), alt.value(2)),
|
||||
)
|
||||
if not overlay_point:
|
||||
highlight_pts = alt.selection_point(
|
||||
on="mouseover",
|
||||
nearest=True,
|
||||
clear="mouseout",
|
||||
empty=False,
|
||||
)
|
||||
points = points.add_params(highlight_pts)
|
||||
|
||||
lines = lines.encode(
|
||||
size=alt.condition(highlight, alt.value(4), alt.value(1)),
|
||||
points = points.encode(
|
||||
opacity=alt.condition(highlight_pts, alt.value(1), alt.value(0)),
|
||||
size=alt.condition(highlight_pts, alt.value(100), alt.value(0)),
|
||||
)
|
||||
|
||||
chart = (lines + points).properties(background="transparent", **properties)
|
||||
if interactive:
|
||||
chart = chart.interactive()
|
||||
|
||||
selection = alt.selection_interval(
|
||||
encodings=["x"],
|
||||
mark=alt.BrushConfig(fill="gray", fillOpacity=0.3, stroke="none"),
|
||||
name="brush",
|
||||
)
|
||||
chart = chart.add_params(selection)
|
||||
|
||||
return chart
|
||||
|
||||
@ -350,7 +371,6 @@ class LinePlot(Plot):
|
||||
x_lim=self.x_lim,
|
||||
y_lim=self.y_lim,
|
||||
stroke_dash=self.stroke_dash,
|
||||
interactive=self.interactive_chart,
|
||||
height=self.height,
|
||||
width=self.width,
|
||||
)
|
||||
|
@ -532,6 +532,7 @@ class EventListener(str):
|
||||
|
||||
event_trigger.event_name = _event_name
|
||||
event_trigger.has_trigger = _has_trigger
|
||||
event_trigger.callback = _callback
|
||||
return event_trigger
|
||||
|
||||
|
||||
@ -600,10 +601,10 @@ def on(
|
||||
"""
|
||||
from gradio.components.base import Component
|
||||
|
||||
triggers_typed = cast(EventListener, triggers)
|
||||
if not isinstance(triggers, Sequence) and triggers is not None:
|
||||
triggers = [triggers]
|
||||
triggers_typed = cast(Sequence[EventListener], triggers)
|
||||
|
||||
if isinstance(triggers_typed, EventListener):
|
||||
triggers_typed = [triggers_typed]
|
||||
if isinstance(inputs, Component):
|
||||
inputs = [inputs]
|
||||
|
||||
@ -654,6 +655,10 @@ def on(
|
||||
EventListenerMethod(t.__self__ if t.has_trigger else None, t.event_name) # type: ignore
|
||||
for t in triggers_typed
|
||||
]
|
||||
if triggers:
|
||||
for trigger in triggers:
|
||||
if trigger.callback:
|
||||
trigger.callback(trigger.__self__)
|
||||
|
||||
if every is not None:
|
||||
from gradio.components import Timer
|
||||
|
@ -0,0 +1,89 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {get_object} from "../../process_json.ts";
|
||||
import ParamTable from "$lib/components/ParamTable.svelte";
|
||||
import ShortcutTable from "$lib/components/ShortcutTable.svelte";
|
||||
import DemosSection from "$lib/components/DemosSection.svelte";
|
||||
import FunctionsSection from "$lib/components/FunctionsSection.svelte";
|
||||
import GuidesSection from "$lib/components/GuidesSection.svelte";
|
||||
import CopyButton from "$lib/components/CopyButton.svelte";
|
||||
import { style_formatted_text } from "$lib/text";
|
||||
|
||||
let obj = get_object("textbox");
|
||||
</script>
|
||||
|
||||
<!--- Title -->
|
||||
# {obj.name}
|
||||
|
||||
<!--- Usage -->
|
||||
```python
|
||||
gradio.DateTime(···)
|
||||
```
|
||||
|
||||
<!-- Embedded Component -->
|
||||
<div class="embedded-component">
|
||||
<gradio-lite shared-worker>
|
||||
import gradio as gr
|
||||
with gr.Blocks() as demo:
|
||||
gr.DateTime()
|
||||
demo.launch()
|
||||
</gradio-lite>
|
||||
</div>
|
||||
|
||||
|
||||
<!--- Description -->
|
||||
### Description
|
||||
## {@html style_formatted_text(obj.description)}
|
||||
|
||||
<!-- Behavior -->
|
||||
### Behavior
|
||||
## **As input component**: {@html style_formatted_text(obj.preprocess.return_doc.doc)}
|
||||
##### Your function should accept one of these types:
|
||||
|
||||
```python
|
||||
def predict(
|
||||
value: float | datetime | str | None
|
||||
)
|
||||
...
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## **As output component**: {@html style_formatted_text(obj.postprocess.parameter_doc[0].doc)}
|
||||
##### Your function should return one of these types:
|
||||
|
||||
```python
|
||||
def predict(···) -> float | datetime | str | None
|
||||
...
|
||||
return value
|
||||
```
|
||||
|
||||
|
||||
<!--- Initialization -->
|
||||
### Initialization
|
||||
<ParamTable parameters={obj.parameters} />
|
||||
|
||||
|
||||
{#if obj.string_shortcuts && obj.string_shortcuts.length > 0}
|
||||
<!--- Shortcuts -->
|
||||
### Shortcuts
|
||||
<ShortcutTable shortcuts={obj.string_shortcuts} />
|
||||
{/if}
|
||||
|
||||
{#if obj.demos && obj.demos.length > 0}
|
||||
<!--- Demos -->
|
||||
### Demos
|
||||
<DemosSection demos={obj.demos} />
|
||||
{/if}
|
||||
|
||||
{#if obj.fns && obj.fns.length > 0}
|
||||
<!--- Event Listeners -->
|
||||
### Event Listeners
|
||||
<FunctionsSection fns={obj.fns} event_listeners={true} />
|
||||
{/if}
|
||||
|
||||
{#if obj.guides && obj.guides.length > 0}
|
||||
<!--- Guides -->
|
||||
### Guides
|
||||
<GuidesSection guides={obj.guides}/>
|
||||
{/if}
|
@ -72,6 +72,7 @@
|
||||
"@gradio/tabs": "workspace:^",
|
||||
"@gradio/textbox": "workspace:^",
|
||||
"@gradio/theme": "workspace:^",
|
||||
"@gradio/datetime": "workspace:^",
|
||||
"@gradio/timer": "workspace:^",
|
||||
"@gradio/upload": "workspace:^",
|
||||
"@gradio/uploadbutton": "workspace:^",
|
||||
|
53
js/app/test/datetimes.spec.ts
Normal file
53
js/app/test/datetimes.spec.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import { test, expect } from "@gradio/tootils";
|
||||
|
||||
test("gr.DateTime shows correct values", async ({ page }) => {
|
||||
await page.locator("#date1").getByRole("textbox").first().click();
|
||||
await page
|
||||
.locator("#date1")
|
||||
.getByRole("textbox")
|
||||
.first()
|
||||
.fill("2020-10-01 10:50:00");
|
||||
await page.locator("body").first().click();
|
||||
await expect(page.getByLabel("Last Change")).toHaveValue(
|
||||
"2020-10-01 10:50:00"
|
||||
);
|
||||
await expect(page.getByLabel("Last Submit")).toHaveValue("");
|
||||
await page.locator("#date1").getByRole("textbox").first().press("Enter");
|
||||
await expect(page.getByLabel("Last Submit")).toHaveValue(
|
||||
"2020-10-01 10:50:00"
|
||||
);
|
||||
await expect(page.getByLabel("Last Load")).toHaveValue("");
|
||||
|
||||
await page.locator("#date2").getByRole("textbox").first().click();
|
||||
await page.locator("#date2").getByRole("textbox").first().fill("2000-02-22");
|
||||
await page.locator("body").first().click();
|
||||
await expect(page.getByLabel("Last Change")).toHaveValue("2000-02-22");
|
||||
|
||||
await page.getByRole("button", { name: "Load Date 1" }).click();
|
||||
await expect(page.getByLabel("Last Load")).toHaveValue("2020-10-01 10:50:00");
|
||||
await page.getByRole("button", { name: "Load Date 2" }).click();
|
||||
await expect(page.getByLabel("Last Load")).toHaveValue("2000-02-22");
|
||||
|
||||
await page.locator("#date2").getByRole("textbox").first().click();
|
||||
await page
|
||||
.locator("#date2")
|
||||
.getByRole("textbox")
|
||||
.first()
|
||||
.fill("2020-05-01xxx");
|
||||
await page.locator("body").first().click();
|
||||
await expect(page.getByLabel("Last Change")).toHaveValue("2000-02-22");
|
||||
|
||||
await page.locator("#date2").getByRole("textbox").first().click();
|
||||
await page.locator("#date2").getByRole("textbox").first().fill("2020-05-02");
|
||||
await page.locator("body").first().click();
|
||||
await expect(page.getByLabel("Last Change")).toHaveValue("2020-05-02");
|
||||
|
||||
await page.locator("#date3").getByRole("textbox").first().click();
|
||||
await page
|
||||
.locator("#date3")
|
||||
.getByRole("textbox")
|
||||
.first()
|
||||
.fill("2020-10-10 05:01:01");
|
||||
await page.getByRole("button", { name: "Load Date 3" }).click();
|
||||
await expect(page.getByLabel("Last Load")).toHaveValue("1602298861.0");
|
||||
});
|
3
js/datetime/CHANGELOG.md
Normal file
3
js/datetime/CHANGELOG.md
Normal file
@ -0,0 +1,3 @@
|
||||
# @gradio/datetime
|
||||
|
||||
## 0.1.0
|
5
js/datetime/Example.svelte
Normal file
5
js/datetime/Example.svelte
Normal file
@ -0,0 +1,5 @@
|
||||
<script lang="ts">
|
||||
export let value: string | null;
|
||||
</script>
|
||||
|
||||
{value || ""}
|
204
js/datetime/Index.svelte
Normal file
204
js/datetime/Index.svelte
Normal file
@ -0,0 +1,204 @@
|
||||
<script context="module" lang="ts">
|
||||
export { default as BaseExample } from "./Example.svelte";
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { Gradio } from "@gradio/utils";
|
||||
import { Block, BlockTitle } from "@gradio/atoms";
|
||||
import { Back, Calendar } from "@gradio/icons";
|
||||
|
||||
export let gradio: Gradio<{
|
||||
change: undefined;
|
||||
submit: undefined;
|
||||
}>;
|
||||
export let label = "Time";
|
||||
export let show_label = true;
|
||||
export let info: string | undefined = undefined;
|
||||
export let elem_id = "";
|
||||
export let elem_classes: string[] = [];
|
||||
export let visible = true;
|
||||
export let value = "";
|
||||
let old_value = value;
|
||||
export let scale: number | null = null;
|
||||
export let min_width: number | undefined = undefined;
|
||||
|
||||
export let include_time = true;
|
||||
$: if (value !== old_value) {
|
||||
old_value = value;
|
||||
gradio.dispatch("change");
|
||||
}
|
||||
|
||||
const format_date = (date: Date): string => {
|
||||
if (date.toJSON() === null) return "";
|
||||
const pad = (num: number): string => num.toString().padStart(2, "0");
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1); // getMonth() returns 0-11
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
const seconds = pad(date.getSeconds());
|
||||
|
||||
const date_str = `${year}-${month}-${day}`;
|
||||
const time_str = `${hours}:${minutes}:${seconds}`;
|
||||
if (include_time) {
|
||||
return `${date_str} ${time_str}`;
|
||||
}
|
||||
return date_str;
|
||||
};
|
||||
|
||||
let entered_value = value;
|
||||
let datetime: HTMLInputElement;
|
||||
let datevalue = "";
|
||||
|
||||
const date_is_valid_format = (date: string): boolean => {
|
||||
if (date === "") return false;
|
||||
const valid_regex = include_time
|
||||
? /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/
|
||||
: /^\d{4}-\d{2}-\d{2}$/;
|
||||
const is_valid_date = date.match(valid_regex) !== null;
|
||||
const is_valid_now =
|
||||
date.match(/^(?:\s*now\s*(?:-\s*\d+\s*[dmhs])?)?\s*$/) !== null;
|
||||
return is_valid_date || is_valid_now;
|
||||
};
|
||||
|
||||
$: valid = date_is_valid_format(entered_value);
|
||||
|
||||
const submit_values = (): void => {
|
||||
if (entered_value === value) return;
|
||||
if (!date_is_valid_format(entered_value)) return;
|
||||
old_value = value = entered_value;
|
||||
gradio.dispatch("change");
|
||||
};
|
||||
</script>
|
||||
|
||||
<Block
|
||||
{visible}
|
||||
{elem_id}
|
||||
{elem_classes}
|
||||
{scale}
|
||||
{min_width}
|
||||
allow_overflow={false}
|
||||
padding={true}
|
||||
>
|
||||
<div class="label-content">
|
||||
<BlockTitle {show_label} {info}>{label}</BlockTitle>
|
||||
</div>
|
||||
<div class="timebox">
|
||||
<input
|
||||
class="time"
|
||||
bind:value={entered_value}
|
||||
class:invalid={!valid}
|
||||
on:keydown={(evt) => {
|
||||
if (evt.key === "Enter") {
|
||||
submit_values();
|
||||
gradio.dispatch("submit");
|
||||
}
|
||||
}}
|
||||
on:blur={submit_values}
|
||||
/>
|
||||
{#if include_time}
|
||||
<input
|
||||
type="datetime-local"
|
||||
class="datetime"
|
||||
step="1"
|
||||
bind:this={datetime}
|
||||
bind:value={datevalue}
|
||||
on:input={() => {
|
||||
const date = new Date(datevalue);
|
||||
entered_value = format_date(date);
|
||||
submit_values();
|
||||
}}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
type="date"
|
||||
class="datetime"
|
||||
step="1"
|
||||
bind:this={datetime}
|
||||
bind:value={datevalue}
|
||||
on:input={() => {
|
||||
const date = new Date(datevalue);
|
||||
entered_value = format_date(date);
|
||||
submit_values();
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="calendar"
|
||||
on:click={() => {
|
||||
datetime.showPicker();
|
||||
}}><Calendar></Calendar></button
|
||||
>
|
||||
</div>
|
||||
</Block>
|
||||
|
||||
<style>
|
||||
.label-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
color: var(--body-text-color-subdued);
|
||||
}
|
||||
button:hover {
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: var(--input-placeholder-color);
|
||||
}
|
||||
.timebox {
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
box-shadow: var(--input-shadow);
|
||||
background: var(--input-background-fill);
|
||||
}
|
||||
.timebox :global(svg) {
|
||||
height: 18px;
|
||||
}
|
||||
.time {
|
||||
padding: var(--input-padding);
|
||||
color: var(--body-text-color);
|
||||
font-weight: var(--input-text-weight);
|
||||
font-size: var(--input-text-size);
|
||||
line-height: var(--line-sm);
|
||||
outline: none;
|
||||
flex-grow: 1;
|
||||
background: none;
|
||||
border: var(--input-border-width) solid var(--input-border-color);
|
||||
border-right: none;
|
||||
border-top-left-radius: var(--input-radius);
|
||||
border-bottom-left-radius: var(--input-radius);
|
||||
}
|
||||
.time.invalid {
|
||||
color: var(--body-text-color-subdued);
|
||||
}
|
||||
.calendar {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: var(--button-transition);
|
||||
box-shadow: var(--button-shadow);
|
||||
text-align: center;
|
||||
background: var(--button-secondary-background-fill);
|
||||
color: var(--button-secondary-text-color);
|
||||
font-weight: var(--button-large-text-weight);
|
||||
font-size: var(--button-large-text-size);
|
||||
border-top-right-radius: var(--input-radius);
|
||||
border-bottom-right-radius: var(--input-radius);
|
||||
padding: var(--size-2);
|
||||
}
|
||||
.datetime {
|
||||
width: 0px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
background: none;
|
||||
}
|
||||
</style>
|
25
js/datetime/package.json
Normal file
25
js/datetime/package.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "@gradio/datetime",
|
||||
"version": "0.0.1",
|
||||
"description": "Gradio UI packages",
|
||||
"type": "module",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": false,
|
||||
"main_changeset": true,
|
||||
"main": "Index.svelte",
|
||||
"exports": {
|
||||
".": "./Index.svelte",
|
||||
"./example": "./Example.svelte",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gradio/atoms": "workspace:^",
|
||||
"@gradio/icons": "workspace:^",
|
||||
"@gradio/statustracker": "workspace:^",
|
||||
"@gradio/utils": "workspace:^"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gradio/preview": "workspace:^"
|
||||
}
|
||||
}
|
17
js/icons/src/Back.svelte
Normal file
17
js/icons/src/Back.svelte
Normal file
@ -0,0 +1,17 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="12px"
|
||||
height="24px"
|
||||
fill="currentColor"
|
||||
stroke-width="1.5"
|
||||
viewBox="0 0 12 24"
|
||||
>
|
||||
<path
|
||||
d="M9 6L3 12L9 18"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 281 B |
51
js/icons/src/Calendar.svelte
Normal file
51
js/icons/src/Calendar.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24px"
|
||||
height="24px"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<rect
|
||||
x="2"
|
||||
y="4"
|
||||
width="20"
|
||||
height="18"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<line
|
||||
x1="2"
|
||||
y1="9"
|
||||
x2="22"
|
||||
y2="9"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<line
|
||||
x1="7"
|
||||
y1="2"
|
||||
x2="7"
|
||||
y2="6"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
<line
|
||||
x1="17"
|
||||
y1="2"
|
||||
x2="17"
|
||||
y2="6"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
fill="none"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 730 B |
@ -1,6 +1,8 @@
|
||||
export { default as Back } from "./Back.svelte";
|
||||
export { default as Backward } from "./Backward.svelte";
|
||||
export { default as Brush } from "./Brush.svelte";
|
||||
export { default as BrushSize } from "./BrushSize.svelte";
|
||||
export { default as Calendar } from "./Calendar.svelte";
|
||||
export { default as Camera } from "./Camera.svelte";
|
||||
export { default as Chart } from "./Chart.svelte";
|
||||
export { default as Chat } from "./Chat.svelte";
|
||||
|
@ -3,7 +3,7 @@
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { Gradio } from "@gradio/utils";
|
||||
import type { Gradio, SelectData } from "@gradio/utils";
|
||||
import Plot from "./shared/Plot.svelte";
|
||||
|
||||
import { Block, BlockLabel } from "@gradio/atoms";
|
||||
@ -31,8 +31,11 @@
|
||||
export let gradio: Gradio<{
|
||||
change: never;
|
||||
clear_status: LoadingStatus;
|
||||
select: SelectData;
|
||||
}>;
|
||||
export let show_actions_button = false;
|
||||
export let _selectable = false;
|
||||
export let x_lim: [number, number] | null = null;
|
||||
</script>
|
||||
|
||||
<Block
|
||||
@ -63,6 +66,10 @@
|
||||
{caption}
|
||||
{bokeh_version}
|
||||
{show_actions_button}
|
||||
{gradio}
|
||||
{_selectable}
|
||||
{x_lim}
|
||||
on:change={() => gradio.dispatch("change")}
|
||||
on:select={(e) => gradio.dispatch("select", e.detail)}
|
||||
/>
|
||||
</Block>
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { Plot as PlotIcon } from "@gradio/icons";
|
||||
import { Empty } from "@gradio/atoms";
|
||||
import type { ThemeMode } from "js/app/src/components/types";
|
||||
import type { Gradio, SelectData } from "@gradio/utils";
|
||||
|
||||
export let value;
|
||||
export let target: HTMLElement;
|
||||
@ -11,6 +12,11 @@
|
||||
export let caption: string;
|
||||
export let bokeh_version: string | null;
|
||||
export let show_actions_button: bool;
|
||||
export let gradio: Gradio<{
|
||||
select: SelectData;
|
||||
}>;
|
||||
export let x_lim: [number, number] | null = null;
|
||||
export let _selectable: boolean;
|
||||
|
||||
let PlotComponent: any = null;
|
||||
let _type = value?.type;
|
||||
@ -45,7 +51,11 @@
|
||||
{caption}
|
||||
{bokeh_version}
|
||||
{show_actions_button}
|
||||
{gradio}
|
||||
{_selectable}
|
||||
{x_lim}
|
||||
on:load
|
||||
on:select
|
||||
/>
|
||||
{:else}
|
||||
<Empty unpadded_box={true} size="large"><PlotIcon /></Empty>
|
||||
|
@ -1,17 +1,24 @@
|
||||
<script lang="ts">
|
||||
//@ts-nocheck
|
||||
import { set_config } from "./altair_utils";
|
||||
import { afterUpdate, onDestroy } from "svelte";
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import type { TopLevelSpec as Spec } from "vega-lite";
|
||||
import vegaEmbed from "vega-embed";
|
||||
import type { Gradio, SelectData } from "@gradio/utils";
|
||||
import type { View } from "vega";
|
||||
|
||||
export let value;
|
||||
export let target: HTMLDivElement;
|
||||
export let colors: string[] = [];
|
||||
export let caption: string;
|
||||
export let show_actions_button: bool;
|
||||
export let gradio: Gradio<{
|
||||
select: SelectData;
|
||||
}>;
|
||||
let element: HTMLElement;
|
||||
let parent_element: HTMLElement;
|
||||
let view: View;
|
||||
export let _selectable: bool;
|
||||
|
||||
let computed_style = window.getComputedStyle(target);
|
||||
|
||||
@ -19,6 +26,9 @@
|
||||
let spec_width: number;
|
||||
$: plot = value?.plot;
|
||||
$: spec = JSON.parse(plot) as Spec;
|
||||
$: if (spec && spec.params && !_selectable) {
|
||||
spec.params = spec.params.filter((param) => param.name !== "brush");
|
||||
}
|
||||
$: if (old_spec !== spec) {
|
||||
old_spec = spec;
|
||||
spec_width = spec.width;
|
||||
@ -34,21 +44,58 @@
|
||||
? false
|
||||
: true; // vega seems to glitch with width when orientation is set
|
||||
|
||||
const get_width = (): number => {
|
||||
return Math.min(
|
||||
parent_element.offsetWidth,
|
||||
spec_width || parent_element.offsetWidth
|
||||
);
|
||||
};
|
||||
let resize_callback = (): void => {};
|
||||
const renderPlot = (): void => {
|
||||
if (fit_width_to_parent) {
|
||||
spec.width = Math.min(
|
||||
parent_element.offsetWidth,
|
||||
spec_width || parent_element.offsetWidth
|
||||
);
|
||||
spec.width = get_width();
|
||||
}
|
||||
vegaEmbed(element, spec, { actions: show_actions_button });
|
||||
vegaEmbed(element, spec, { actions: show_actions_button }).then(
|
||||
function (result): void {
|
||||
view = result.view;
|
||||
resize_callback = () => {
|
||||
view.signal("width", get_width()).run();
|
||||
};
|
||||
|
||||
if (!_selectable) return;
|
||||
const callback = (event, item): void => {
|
||||
const brushValue = view.signal("brush");
|
||||
if (brushValue) {
|
||||
if (Object.keys(brushValue).length === 0) {
|
||||
gradio.dispatch("select", {
|
||||
value: null,
|
||||
index: null,
|
||||
selected: false
|
||||
});
|
||||
} else {
|
||||
const key = Object.keys(brushValue)[0];
|
||||
let range: [number, number] = brushValue[key].map(
|
||||
(x) => x / 1000
|
||||
);
|
||||
gradio.dispatch("select", {
|
||||
value: brushValue,
|
||||
index: range,
|
||||
selected: true
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
view.addEventListener("mouseup", callback);
|
||||
view.addEventListener("touchup", callback);
|
||||
}
|
||||
);
|
||||
};
|
||||
let resizeObserver = new ResizeObserver(() => {
|
||||
if (fit_width_to_parent && spec.width !== parent_element.offsetWidth) {
|
||||
renderPlot();
|
||||
resize_callback();
|
||||
}
|
||||
});
|
||||
afterUpdate(() => {
|
||||
onMount(() => {
|
||||
renderPlot();
|
||||
resizeObserver.observe(parent_element);
|
||||
});
|
||||
|
@ -77,7 +77,7 @@ export function set_config(
|
||||
}
|
||||
break;
|
||||
case "line":
|
||||
spec.config.mark = { stroke: accentColor };
|
||||
spec.config.mark = { stroke: accentColor, cursor: "crosshair" };
|
||||
layer.forEach((d: any) => {
|
||||
if (d.encoding.color) {
|
||||
d.encoding.color.scale.range = d.encoding.color.scale.range.map(
|
||||
|
@ -133,6 +133,7 @@
|
||||
"@gradio/tabitem": "workspace:^",
|
||||
"@gradio/tabs": "workspace:^",
|
||||
"@gradio/textbox": "workspace:^",
|
||||
"@gradio/datetime": "workspace:^",
|
||||
"@gradio/timer": "workspace:^",
|
||||
"@gradio/upload": "workspace:^",
|
||||
"@gradio/uploadbutton": "workspace:^",
|
||||
|
@ -216,6 +216,9 @@ importers:
|
||||
'@gradio/dataset':
|
||||
specifier: workspace:^
|
||||
version: link:js/dataset
|
||||
'@gradio/datetime':
|
||||
specifier: workspace:^
|
||||
version: link:js/datetime
|
||||
'@gradio/downloadbutton':
|
||||
specifier: workspace:^
|
||||
version: link:js/downloadbutton
|
||||
@ -562,6 +565,9 @@ importers:
|
||||
'@gradio/dataset':
|
||||
specifier: workspace:^
|
||||
version: link:../dataset
|
||||
'@gradio/datetime':
|
||||
specifier: workspace:^
|
||||
version: link:../datetime
|
||||
'@gradio/downloadbutton':
|
||||
specifier: workspace:^
|
||||
version: link:../downloadbutton
|
||||
@ -1061,6 +1067,25 @@ importers:
|
||||
specifier: workspace:^
|
||||
version: link:../preview
|
||||
|
||||
js/datetime:
|
||||
dependencies:
|
||||
'@gradio/atoms':
|
||||
specifier: workspace:^
|
||||
version: link:../atoms
|
||||
'@gradio/icons':
|
||||
specifier: workspace:^
|
||||
version: link:../icons
|
||||
'@gradio/statustracker':
|
||||
specifier: workspace:^
|
||||
version: link:../statustracker
|
||||
'@gradio/utils':
|
||||
specifier: workspace:^
|
||||
version: link:../utils
|
||||
devDependencies:
|
||||
'@gradio/preview':
|
||||
specifier: workspace:^
|
||||
version: link:../preview
|
||||
|
||||
js/downloadbutton:
|
||||
dependencies:
|
||||
'@gradio/button':
|
||||
|
@ -1,5 +1,5 @@
|
||||
aiofiles>=22.0,<24.0
|
||||
altair>=4.2.0,<6.0
|
||||
altair>=5.0,<6.0
|
||||
fastapi
|
||||
ffmpy
|
||||
gradio_client==1.0.2
|
||||
|
43
test/components/test_datetime.py
Normal file
43
test/components/test_datetime.py
Normal file
@ -0,0 +1,43 @@
|
||||
from datetime import datetime
|
||||
|
||||
import gradio as gr
|
||||
|
||||
|
||||
class TestDateTime:
|
||||
def test_component_functions(self):
|
||||
"""
|
||||
Preprocess, postprocess
|
||||
"""
|
||||
|
||||
def within_range(time1, time2, delta=30):
|
||||
return abs(time1 - time2) < delta
|
||||
|
||||
dt = gr.DateTime(timezone="US/Pacific")
|
||||
dt2 = gr.DateTime(timezone="Europe/Paris")
|
||||
dt3 = gr.DateTime(timezone="Europe/Paris", include_time=False)
|
||||
dt4 = gr.DateTime(timezone="US/Pacific", type="string")
|
||||
now = datetime.now().timestamp()
|
||||
assert dt.preprocess("2020-02-01 08:10:25") == 1580573425.0
|
||||
assert dt2.preprocess("2020-02-01 08:10:25") == 1580541025.0
|
||||
assert dt3.preprocess("2020-02-01") == 1580511600.0
|
||||
assert within_range(dt.preprocess("now"), now)
|
||||
assert not within_range(dt.preprocess("now - 1m"), now)
|
||||
assert within_range(dt2.preprocess("now"), now)
|
||||
assert within_range(dt.preprocess("now - 20s"), now - 20)
|
||||
assert within_range(dt2.preprocess("now - 20s"), now - 20)
|
||||
assert within_range(dt.preprocess("now - 10m"), now - 10 * 60)
|
||||
assert within_range(dt.preprocess("now - 3h"), now - 3 * 60 * 60)
|
||||
assert within_range(dt.preprocess("now - 12d"), now - 12 * 24 * 60 * 60)
|
||||
assert dt4.preprocess("2020-02-01 08:10:25") == "2020-02-01 08:10:25"
|
||||
assert len(dt4.preprocess("now - 10m")) == 19
|
||||
|
||||
assert dt.postprocess(1500000000) == "2017-07-13 19:40:00"
|
||||
assert dt2.postprocess(1500000000) == "2017-07-14 04:40:00"
|
||||
|
||||
def test_in_interface(self):
|
||||
"""
|
||||
Interface, process
|
||||
"""
|
||||
dt = gr.DateTime(timezone="US/Pacific")
|
||||
iface = gr.Interface(lambda x: x, dt, "number")
|
||||
assert iface("2017-07-13 19:40:00") == 1500000000
|
@ -31,7 +31,7 @@ class TestLinePlot:
|
||||
"stroke_dash": None,
|
||||
"overlay_point": None,
|
||||
"title": None,
|
||||
"tooltip": None,
|
||||
"tooltip": [],
|
||||
"x_title": None,
|
||||
"y_title": None,
|
||||
"color_legend_title": None,
|
||||
|
@ -6,7 +6,7 @@
|
||||
#
|
||||
aiofiles==23.2.1
|
||||
# via gradio
|
||||
altair==4.2.0
|
||||
altair>=5.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# gradio
|
||||
|
Loading…
Reference in New Issue
Block a user