mirror of
https://github.com/gradio-app/gradio.git
synced 2025-04-12 12:40:29 +08:00
Adding the .select event listener (#3399)
* changes * changes * changes * changes * changes * changes * changs * changes * changes * changes * changes * changes * changes * preview * fix notebook * changes * changes * changes * changes * changes * changes * changes * changes * Add type hints (#3403) Co-authored-by: aliabid94 <aabid94@gmail.com> * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * changes * change * changes * changes * changes * changes * changes * changes * Update gradio/helpers.py Co-authored-by: Abubakar Abid <abubakar@huggingface.co> * doc update * changes * changes * changes --------- Co-authored-by: Abubakar Abid <abubakar@huggingface.co> Co-authored-by: Freddy Boulton <alfonsoboulton@gmail.com>
This commit is contained in:
parent
175dd160ec
commit
95bf08b2e2
16
CHANGELOG.md
16
CHANGELOG.md
@ -25,6 +25,22 @@ Note: images were previously supported via Markdown syntax and that is still sup
|
||||
- New code component allows you to enter, edit and display code with full syntax highlighting by [@pngwn](https://github.com/pngwn) in [PR 3421](https://github.com/gradio-app/gradio/pull/3421)
|
||||
|
||||

|
||||
- Added the `.select()` event listener, which also includes event data that can be passed as an argument to a function with type hint `gr.SelectData`. The following components support the `.select()` event listener: Chatbot, CheckboxGroup, Dataframe, Dropdown, File, Gallery, HighlightedText, Label, Radio, TabItem, Tab, Textbox. Example usage:
|
||||
|
||||
```python
|
||||
import gradio as gr
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
gallery = gr.Gallery(["images/1.jpg", "images/2.jpg", "images/3.jpg"])
|
||||
selected_index = gr.Textbox()
|
||||
|
||||
def on_select(evt: gr.SelectData):
|
||||
return evt.index
|
||||
|
||||
gallery.select(on_select, None, selected_index)
|
||||
```
|
||||
|
||||
By [@aliabid94](https://github.com/aliabid94) in [PR 3399](https://github.com/gradio-app/gradio/pull/3399)
|
||||
|
||||
|
||||
## Bug Fixes:
|
||||
|
File diff suppressed because one or more lines are too long
@ -70,7 +70,7 @@ with gr.Blocks() as demo:
|
||||
)
|
||||
|
||||
name = gr.Textbox(
|
||||
label="Name",
|
||||
label="Name (select)",
|
||||
info="Full name, including middle name. No special characters.",
|
||||
placeholder="John Doe",
|
||||
value="John Doe",
|
||||
@ -80,14 +80,14 @@ with gr.Blocks() as demo:
|
||||
with gr.Row():
|
||||
slider1 = gr.Slider(label="Slider 1")
|
||||
slider2 = gr.Slider(label="Slider 2")
|
||||
gr.CheckboxGroup(["A", "B", "C"], label="Checkbox Group")
|
||||
checkboxes = gr.CheckboxGroup(["A", "B", "C"], label="Checkbox Group (select)")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(variant="panel", scale=1):
|
||||
gr.Markdown("## Panel 1")
|
||||
radio = gr.Radio(
|
||||
["A", "B", "C"],
|
||||
label="Radio",
|
||||
label="Radio (select)",
|
||||
info="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.",
|
||||
)
|
||||
drop = gr.Dropdown(["Option 1", "Option 2", "Option 3"], show_label=False)
|
||||
@ -95,7 +95,7 @@ with gr.Blocks() as demo:
|
||||
["Option A", "Option B", "Option C"],
|
||||
multiselect=True,
|
||||
value=["Option A"],
|
||||
label="Dropdown",
|
||||
label="Dropdown (select)",
|
||||
interactive=True,
|
||||
)
|
||||
check = gr.Checkbox(label="Go")
|
||||
@ -145,46 +145,59 @@ with gr.Blocks() as demo:
|
||||
],
|
||||
],
|
||||
inputs=[radio, drop, drop_2, check, img],
|
||||
label="Examples (select)",
|
||||
)
|
||||
|
||||
gr.Markdown("## Media Files")
|
||||
|
||||
with gr.Tab("Audio"):
|
||||
with gr.Row():
|
||||
gr.Audio()
|
||||
gr.Audio(source="microphone")
|
||||
gr.Audio(join(KS_FILES, "cantina.wav"))
|
||||
with gr.Tab("Other"):
|
||||
# gr.Image(source="webcam")
|
||||
gr.HTML(
|
||||
"<div style='width: 100px; height: 100px; background-color: blue;'></div>"
|
||||
)
|
||||
with gr.Tabs() as tabs:
|
||||
with gr.Tab("Audio"):
|
||||
with gr.Row():
|
||||
gr.Audio()
|
||||
gr.Audio(source="microphone")
|
||||
gr.Audio(join(KS_FILES, "cantina.wav"))
|
||||
with gr.Tab("Other"):
|
||||
# gr.Image(source="webcam")
|
||||
gr.HTML(
|
||||
"<div style='width: 100px; height: 100px; background-color: blue;'></div>"
|
||||
)
|
||||
with gr.Row():
|
||||
gr.Dataframe(value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]], label="Dataframe")
|
||||
dataframe = gr.Dataframe(
|
||||
value=[[1, 2, 3], [4, 5, 6], [7, 8, 9]], label="Dataframe (select)"
|
||||
)
|
||||
gr.JSON(
|
||||
value={"a": 1, "b": 2, "c": {"test": "a", "test2": [1, 2, 3]}}, label="JSON"
|
||||
)
|
||||
gr.Label(value={"cat": 0.7, "dog": 0.2, "fish": 0.1})
|
||||
gr.File()
|
||||
label = gr.Label(
|
||||
value={"cat": 0.7, "dog": 0.2, "fish": 0.1}, label="Label (select)"
|
||||
)
|
||||
file = gr.File(label="File (select)")
|
||||
with gr.Row():
|
||||
gr.ColorPicker()
|
||||
gr.Video(join(KS_FILES, "world.mp4"))
|
||||
gr.Gallery(
|
||||
gallery = gr.Gallery(
|
||||
[
|
||||
(join(KS_FILES, "lion.jpg"), "lion"),
|
||||
(join(KS_FILES, "logo.png"), "logo"),
|
||||
(join(KS_FILES, "tower.jpg"), "tower"),
|
||||
]
|
||||
],
|
||||
label="Gallery (select)",
|
||||
)
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=2):
|
||||
chatbot = gr.Chatbot([["Hello", "Hi"]], label="Chatbot")
|
||||
highlight = gr.HighlightedText(
|
||||
[["The", "art"], ["dog", "noun"], ["is", None], ["fat", "adj"]],
|
||||
label="Highlighted Text (select)",
|
||||
)
|
||||
chatbot = gr.Chatbot([["Hello", "Hi"]], label="Chatbot (select)")
|
||||
chat_btn = gr.Button("Add messages")
|
||||
|
||||
def chat(history):
|
||||
time.sleep(2)
|
||||
yield [["How are you?", "I am good."]]
|
||||
time
|
||||
|
||||
chat_btn.click(
|
||||
lambda history: history
|
||||
+ [["How are you?", "I am good."]]
|
||||
@ -193,11 +206,41 @@ with gr.Blocks() as demo:
|
||||
chatbot,
|
||||
)
|
||||
with gr.Column(scale=1):
|
||||
with gr.Accordion("Advanced Settings"):
|
||||
gr.Markdown("Hello")
|
||||
gr.Number(label="Chatbot control 1")
|
||||
gr.Number(label="Chatbot control 2")
|
||||
gr.Number(label="Chatbot control 3")
|
||||
with gr.Accordion("Select Info"):
|
||||
gr.Markdown(
|
||||
"Click on any part of any component with '(select)' in the label and see the SelectData data here."
|
||||
)
|
||||
select_index = gr.Textbox(label="Index")
|
||||
select_value = gr.Textbox(label="Value")
|
||||
select_selected = gr.Textbox(label="Selected")
|
||||
|
||||
selectables = [
|
||||
name,
|
||||
checkboxes,
|
||||
radio,
|
||||
drop_2,
|
||||
dataframe,
|
||||
label,
|
||||
file,
|
||||
highlight,
|
||||
chatbot,
|
||||
gallery,
|
||||
tabs
|
||||
]
|
||||
|
||||
def select_data(evt: gr.SelectData):
|
||||
return [
|
||||
evt.index,
|
||||
evt.value,
|
||||
evt.selected,
|
||||
]
|
||||
|
||||
for selectable in selectables:
|
||||
selectable.select(
|
||||
select_data,
|
||||
None,
|
||||
[select_index, select_value, select_selected],
|
||||
)
|
||||
|
||||
gr.Markdown("## Dataset Examples")
|
||||
|
||||
@ -230,4 +273,4 @@ with gr.Blocks() as demo:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.queue().launch(file_directories=[KS_FILES])
|
||||
demo.launch(file_directories=[KS_FILES])
|
||||
|
1
demo/gallery_selections/run.ipynb
Normal file
1
demo/gallery_selections/run.ipynb
Normal file
@ -0,0 +1 @@
|
||||
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: gallery_selections"]}, {"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", "import numpy as np\n", "\n", "with gr.Blocks() as demo:\n", " imgs = gr.State()\n", " gallery = gr.Gallery()\n", "\n", " def generate_images():\n", " images = []\n", " for _ in range(9):\n", " image = np.ones((100, 100, 3), dtype=np.uint8) * np.random.randint(\n", " 0, 255, 3\n", " ) # image is a solid single color\n", " images.append(image)\n", " return images, images\n", "\n", " demo.load(generate_images, None, [gallery, imgs])\n", "\n", " with gr.Row():\n", " selected = gr.Number(show_label=False, placeholder=\"Selected\")\n", " darken_btn = gr.Button(\"Darken selected\")\n", "\n", " def get_select_index(evt: gr.SelectData):\n", " return evt.index\n", "\n", " gallery.select(get_select_index, None, selected)\n", "\n", " def darken_img(imgs, index):\n", " index = int(index)\n", " imgs[index] = np.round(imgs[index] * 0.8).astype(np.uint8)\n", " return imgs, imgs\n", "\n", " darken_btn.click(darken_img, [imgs, selected], [imgs, gallery])\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
36
demo/gallery_selections/run.py
Normal file
36
demo/gallery_selections/run.py
Normal file
@ -0,0 +1,36 @@
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
imgs = gr.State()
|
||||
gallery = gr.Gallery()
|
||||
|
||||
def generate_images():
|
||||
images = []
|
||||
for _ in range(9):
|
||||
image = np.ones((100, 100, 3), dtype=np.uint8) * np.random.randint(
|
||||
0, 255, 3
|
||||
) # image is a solid single color
|
||||
images.append(image)
|
||||
return images, images
|
||||
|
||||
demo.load(generate_images, None, [gallery, imgs])
|
||||
|
||||
with gr.Row():
|
||||
selected = gr.Number(show_label=False, placeholder="Selected")
|
||||
darken_btn = gr.Button("Darken selected")
|
||||
|
||||
def get_select_index(evt: gr.SelectData):
|
||||
return evt.index
|
||||
|
||||
gallery.select(get_select_index, None, selected)
|
||||
|
||||
def darken_img(imgs, index):
|
||||
index = int(index)
|
||||
imgs[index] = np.round(imgs[index] * 0.8).astype(np.uint8)
|
||||
return imgs, imgs
|
||||
|
||||
darken_btn.click(darken_img, [imgs, selected], [imgs, gallery])
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
1
demo/tictactoe/run.ipynb
Normal file
1
demo/tictactoe/run.ipynb
Normal file
@ -0,0 +1 @@
|
||||
{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: tictactoe"]}, {"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", " turn = gr.Textbox(\"X\", interactive=False, label=\"Turn\")\n", " board = gr.Dataframe(value=[[\"\", \"\", \"\"]] * 3, interactive=False, type=\"array\")\n", "\n", " def place(board, turn, evt: gr.SelectData):\n", " if evt.value:\n", " return board, turn\n", " board[evt.index[0]][evt.index[1]] = turn\n", " turn = \"O\" if turn == \"X\" else \"X\"\n", " return board, turn\n", "\n", " board.select(place, [board, turn], [board, turn])\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5}
|
17
demo/tictactoe/run.py
Normal file
17
demo/tictactoe/run.py
Normal file
@ -0,0 +1,17 @@
|
||||
import gradio as gr
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
turn = gr.Textbox("X", interactive=False, label="Turn")
|
||||
board = gr.Dataframe(value=[["", "", ""]] * 3, interactive=False, type="array")
|
||||
|
||||
def place(board, turn, evt: gr.SelectData):
|
||||
if evt.value:
|
||||
return board, turn
|
||||
board[evt.index[0]][evt.index[1]] = turn
|
||||
turn = "O" if turn == "X" else "X"
|
||||
return board, turn
|
||||
|
||||
board.select(place, [board, turn], [board, turn])
|
||||
|
||||
if __name__ == "__main__":
|
||||
demo.launch()
|
@ -52,6 +52,7 @@ from gradio.components import (
|
||||
Video,
|
||||
component,
|
||||
)
|
||||
from gradio.events import SelectData
|
||||
from gradio.exceptions import Error
|
||||
from gradio.flagging import (
|
||||
CSVLogger,
|
||||
@ -60,7 +61,7 @@ from gradio.flagging import (
|
||||
HuggingFaceDatasetSaver,
|
||||
SimpleCSVLogger,
|
||||
)
|
||||
from gradio.helpers import Progress
|
||||
from gradio.helpers import EventData, Progress
|
||||
from gradio.helpers import create_examples as Examples
|
||||
from gradio.helpers import make_waveform, skip, update
|
||||
from gradio.interface import Interface, TabbedInterface, close_all
|
||||
|
@ -24,7 +24,7 @@ from gradio.context import Context
|
||||
from gradio.deprecation import check_deprecated_parameters
|
||||
from gradio.documentation import document, set_documentation_group
|
||||
from gradio.exceptions import DuplicateBlockError, InvalidApiName
|
||||
from gradio.helpers import create_tracker, skip, special_args
|
||||
from gradio.helpers import EventData, create_tracker, skip, special_args
|
||||
from gradio.themes import Default as DefaultTheme
|
||||
from gradio.themes import ThemeClass as Theme
|
||||
from gradio.tunneling import CURRENT_TUNNELS
|
||||
@ -140,6 +140,7 @@ class Block:
|
||||
max_batch_size: int = 4,
|
||||
cancels: List[int] | None = None,
|
||||
every: float | None = None,
|
||||
collects_event_data: bool | None = None,
|
||||
trigger_after: int | None = None,
|
||||
trigger_only_on_success: bool = False,
|
||||
) -> Tuple[Dict[str, Any], int]:
|
||||
@ -161,6 +162,7 @@ class Block:
|
||||
max_batch_size: the maximum batch size to send to the function
|
||||
cancels: a list of other events to cancel when this event is triggered. For example, setting cancels=[click_event] will cancel the click_event, where click_event is the return value of another components .click method.
|
||||
every: Run this event 'every' number of seconds while the client connection is open. Interpreted in seconds. Queue must be enabled.
|
||||
collects_event_data: whether to collect event data for this event
|
||||
trigger_after: if set, this event will be triggered after 'trigger_after' function index
|
||||
trigger_only_on_success: if True, this event will only be triggered if the previous event was successful (only applies if `trigger_after` is set)
|
||||
Returns: dependency information, dependency index
|
||||
@ -204,8 +206,19 @@ class Block:
|
||||
elif every:
|
||||
raise ValueError("Cannot set a value for `every` without a `fn`.")
|
||||
|
||||
_, progress_index, event_data_index = (
|
||||
special_args(fn) if fn else (None, None, None)
|
||||
)
|
||||
Context.root_block.fns.append(
|
||||
BlockFunction(fn, inputs, outputs, preprocess, postprocess, inputs_as_dict)
|
||||
BlockFunction(
|
||||
fn,
|
||||
inputs,
|
||||
outputs,
|
||||
preprocess,
|
||||
postprocess,
|
||||
inputs_as_dict,
|
||||
progress_index is not None,
|
||||
)
|
||||
)
|
||||
if api_name is not None:
|
||||
api_name_ = utils.append_unique_suffix(
|
||||
@ -217,6 +230,9 @@ class Block:
|
||||
)
|
||||
api_name = api_name_
|
||||
|
||||
if collects_event_data is None:
|
||||
collects_event_data = event_data_index is not None
|
||||
|
||||
dependency = {
|
||||
"targets": [self._id] if not no_target else [],
|
||||
"trigger": event_name,
|
||||
@ -236,6 +252,7 @@ class Block:
|
||||
"continuous": bool(every),
|
||||
"generator": inspect.isgeneratorfunction(fn) or bool(every),
|
||||
},
|
||||
"collects_event_data": collects_event_data,
|
||||
"trigger_after": trigger_after,
|
||||
"trigger_only_on_success": trigger_only_on_success,
|
||||
}
|
||||
@ -276,7 +293,7 @@ class BlockContext(Block):
|
||||
render: If False, this will not be included in the Blocks config file at all.
|
||||
"""
|
||||
self.children: List[Block] = []
|
||||
super().__init__(visible=visible, render=render, **kwargs)
|
||||
Block.__init__(self, visible=visible, render=render, **kwargs)
|
||||
|
||||
def __enter__(self):
|
||||
self.parent = Context.block
|
||||
@ -330,12 +347,14 @@ class BlockFunction:
|
||||
preprocess: bool,
|
||||
postprocess: bool,
|
||||
inputs_as_dict: bool,
|
||||
tracks_progress: bool = False,
|
||||
):
|
||||
self.fn = fn
|
||||
self.inputs = inputs
|
||||
self.outputs = outputs
|
||||
self.preprocess = preprocess
|
||||
self.postprocess = postprocess
|
||||
self.tracks_progress = tracks_progress
|
||||
self.total_runtime = 0
|
||||
self.total_runs = 0
|
||||
self.inputs_as_dict = inputs_as_dict
|
||||
@ -797,6 +816,7 @@ class Blocks(BlockContext):
|
||||
iterator: Iterator[Any] | None = None,
|
||||
requests: routes.Request | List[routes.Request] | None = None,
|
||||
event_id: str | None = None,
|
||||
event_data: EventData | None = None,
|
||||
):
|
||||
"""
|
||||
Calls function with given index and preprocessed input, and measures process time.
|
||||
@ -806,6 +826,7 @@ class Blocks(BlockContext):
|
||||
iterator: iterator to use if function is a generator
|
||||
requests: requests to pass to function
|
||||
event_id: id of event in queue
|
||||
event_data: data associated with event trigger
|
||||
"""
|
||||
block_fn = self.fns[fn_index]
|
||||
assert block_fn.fn, f"function with index {fn_index} not defined."
|
||||
@ -823,10 +844,8 @@ class Blocks(BlockContext):
|
||||
request = requests[0]
|
||||
else:
|
||||
request = requests
|
||||
processed_input, progress_index = special_args(
|
||||
block_fn.fn,
|
||||
processed_input,
|
||||
request,
|
||||
processed_input, progress_index, _ = special_args(
|
||||
block_fn.fn, processed_input, request, event_data
|
||||
)
|
||||
progress_tracker = (
|
||||
processed_input[progress_index] if progress_index is not None else None
|
||||
@ -987,6 +1006,7 @@ class Blocks(BlockContext):
|
||||
request: routes.Request | List[routes.Request] | None = None,
|
||||
iterators: Dict[int, Any] | None = None,
|
||||
event_id: str | None = None,
|
||||
event_data: EventData | None = None,
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Processes API calls from the frontend. First preprocesses the data,
|
||||
@ -997,6 +1017,8 @@ class Blocks(BlockContext):
|
||||
username: name of user if authentication is set up (not used)
|
||||
state: data stored from stateful components for session (key is input block id)
|
||||
iterators: the in-progress iterators for each generator function (key is function index)
|
||||
event_id: id of event that triggered this API call
|
||||
event_data: data associated with the event trigger itself
|
||||
Returns: None
|
||||
"""
|
||||
block_fn = self.fns[fn_index]
|
||||
@ -1023,7 +1045,7 @@ class Blocks(BlockContext):
|
||||
self.preprocess_data(fn_index, list(i), state) for i in zip(*inputs)
|
||||
]
|
||||
result = await self.call_function(
|
||||
fn_index, list(zip(*inputs)), None, request
|
||||
fn_index, list(zip(*inputs)), None, request, event_id, event_data
|
||||
)
|
||||
preds = result["prediction"]
|
||||
data = [
|
||||
@ -1035,7 +1057,7 @@ class Blocks(BlockContext):
|
||||
inputs = self.preprocess_data(fn_index, inputs, state)
|
||||
iterator = iterators.get(fn_index, None) if iterators else None
|
||||
result = await self.call_function(
|
||||
fn_index, inputs, iterator, request, event_id
|
||||
fn_index, inputs, iterator, request, event_id, event_data
|
||||
)
|
||||
data = self.postprocess_data(fn_index, result["prediction"], state)
|
||||
is_generating, iterator = result["is_generating"], result["iterator"]
|
||||
@ -1118,10 +1140,7 @@ class Blocks(BlockContext):
|
||||
self.parent.children.extend(self.children)
|
||||
self.config = self.get_config_file()
|
||||
self.app = routes.App.create_app(self)
|
||||
self.progress_tracking = any(
|
||||
block_fn.fn is not None and special_args(block_fn.fn)[1] is not None
|
||||
for block_fn in self.fns
|
||||
)
|
||||
self.progress_tracking = any(block_fn.tracks_progress for block_fn in self.fns)
|
||||
|
||||
@class_or_instancemethod
|
||||
def load(
|
||||
|
@ -39,8 +39,10 @@ from gradio.events import (
|
||||
Clickable,
|
||||
Editable,
|
||||
EventListener,
|
||||
EventListenerMethod,
|
||||
Playable,
|
||||
Releaseable,
|
||||
Selectable,
|
||||
Streamable,
|
||||
Submittable,
|
||||
Uploadable,
|
||||
@ -244,6 +246,7 @@ class FormComponent:
|
||||
class Textbox(
|
||||
FormComponent,
|
||||
Changeable,
|
||||
Selectable,
|
||||
Submittable,
|
||||
Blurrable,
|
||||
IOComponent,
|
||||
@ -299,6 +302,12 @@ class Textbox(
|
||||
self.lines = lines
|
||||
self.max_lines = max_lines if type == "text" else 1
|
||||
self.placeholder = placeholder
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects text in the Textbox.
|
||||
Uses event data gradio.SelectData to carry `value` referring to selected subtring, and `index` tuple referring to selected range endpoints.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -769,15 +778,21 @@ class Slider(
|
||||
Parameters:
|
||||
container: If True, will place the component in a container - providing some extra padding around the border.
|
||||
"""
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
container=container,
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
class Checkbox(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
FormComponent,
|
||||
Changeable,
|
||||
Selectable,
|
||||
IOComponent,
|
||||
SimpleSerializable,
|
||||
NeighborInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates a checkbox that can be set to `True` or `False`.
|
||||
@ -813,6 +828,12 @@ class Checkbox(
|
||||
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
"""
|
||||
self.test_input = True
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects or deselects Checkbox.
|
||||
Uses event data gradio.SelectData to carry `value` referring to label of checkbox, and `selected` to refer to state of checkbox.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -870,7 +891,12 @@ class Checkbox(
|
||||
|
||||
@document("style")
|
||||
class CheckboxGroup(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
FormComponent,
|
||||
Changeable,
|
||||
Selectable,
|
||||
IOComponent,
|
||||
SimpleSerializable,
|
||||
NeighborInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates a set of checkboxes of which a subset can be checked.
|
||||
@ -917,6 +943,12 @@ class CheckboxGroup(
|
||||
)
|
||||
self.type = type
|
||||
self.test_input = self.choices
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects or deselects within CheckboxGroup.
|
||||
Uses event data gradio.SelectData to carry `value` referring to label of selected checkbox, `index` to refer to index, and `selected` to refer to state of checkbox.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -1037,12 +1069,18 @@ class CheckboxGroup(
|
||||
if item_container is not None:
|
||||
self._style["item_container"] = item_container
|
||||
|
||||
return Component.style(self, container=container, **kwargs)
|
||||
Component.style(self, container=container, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
class Radio(
|
||||
FormComponent, Changeable, IOComponent, SimpleSerializable, NeighborInterpretable
|
||||
FormComponent,
|
||||
Selectable,
|
||||
Changeable,
|
||||
IOComponent,
|
||||
SimpleSerializable,
|
||||
NeighborInterpretable,
|
||||
):
|
||||
"""
|
||||
Creates a set of radio buttons of which only one can be selected.
|
||||
@ -1089,6 +1127,12 @@ class Radio(
|
||||
)
|
||||
self.type = type
|
||||
self.test_input = self.choices[0] if len(self.choices) else None
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects Radio option.
|
||||
Uses event data gradio.SelectData to carry `value` referring to label of selected option, and `index` to refer to index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -1186,11 +1230,12 @@ class Radio(
|
||||
if item_container is not None:
|
||||
self._style["item_container"] = item_container
|
||||
|
||||
return Component.style(self, container=container, **kwargs)
|
||||
Component.style(self, container=container, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
class Dropdown(Changeable, Selectable, IOComponent, SimpleSerializable, FormComponent):
|
||||
"""
|
||||
Creates a dropdown of choices from which entries can be selected.
|
||||
Preprocessing: passes the value of the selected dropdown entry as a {str} or its index as an {int} into the function, depending on `type`.
|
||||
@ -1249,6 +1294,12 @@ class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
self.max_choices = max_choices
|
||||
self.test_input = self.choices[0] if len(self.choices) else None
|
||||
self.interpret_by_tokens = False
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects Dropdown option.
|
||||
Uses event data gradio.SelectData to carry `value` referring to label of selected option, and `index` to refer to index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -1349,7 +1400,8 @@ class Dropdown(Changeable, IOComponent, SimpleSerializable, FormComponent):
|
||||
Parameters:
|
||||
container: If True, will place the component in a container - providing some extra padding around the border.
|
||||
"""
|
||||
return Component.style(self, container=container, **kwargs)
|
||||
Component.style(self, container=container, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
@ -1681,10 +1733,11 @@ class Image(
|
||||
"""
|
||||
self._style["height"] = height
|
||||
self._style["width"] = width
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
def stream(
|
||||
self,
|
||||
@ -1939,10 +1992,11 @@ class Video(
|
||||
"""
|
||||
self._style["height"] = height
|
||||
self._style["width"] = width
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
@ -2246,10 +2300,11 @@ class Audio(
|
||||
"""
|
||||
This method can be used to change the appearance of the audio component.
|
||||
"""
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
def as_example(self, input_data: str | None) -> str:
|
||||
return Path(input_data).name if input_data else ""
|
||||
@ -2257,7 +2312,13 @@ class Audio(
|
||||
|
||||
@document("style")
|
||||
class File(
|
||||
Changeable, Clearable, Uploadable, IOComponent, FileSerializable, TempFileManager
|
||||
Changeable,
|
||||
Selectable,
|
||||
Clearable,
|
||||
Uploadable,
|
||||
IOComponent,
|
||||
FileSerializable,
|
||||
TempFileManager,
|
||||
):
|
||||
"""
|
||||
Creates a file component that allows uploading generic file (when used as an input) and or displaying generic files (output).
|
||||
@ -2320,6 +2381,12 @@ class File(
|
||||
)
|
||||
self.type = type
|
||||
self.test_input = None
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects file from list.
|
||||
Uses event data gradio.SelectData to carry `value` referring to name of selected file, and `index` to refer to index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
TempFileManager.__init__(self)
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
@ -2338,6 +2405,7 @@ class File(
|
||||
"file_count": self.file_count,
|
||||
"file_types": self.file_types,
|
||||
"value": self.value,
|
||||
"selectable": self.selectable,
|
||||
**IOComponent.get_config(self),
|
||||
}
|
||||
|
||||
@ -2465,10 +2533,11 @@ class File(
|
||||
"""
|
||||
This method can be used to change the appearance of the file component.
|
||||
"""
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
def as_example(self, input_data: str | List | None) -> str:
|
||||
if input_data is None:
|
||||
@ -2480,7 +2549,7 @@ class File(
|
||||
|
||||
|
||||
@document("style")
|
||||
class Dataframe(Changeable, IOComponent, JSONSerializable):
|
||||
class Dataframe(Changeable, Selectable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
Accepts or displays 2D input through a spreadsheet-like component for dataframes.
|
||||
Preprocessing: passes the uploaded spreadsheet data as a {pandas.DataFrame}, {numpy.array}, {List[List]}, or {List} depending on `type`
|
||||
@ -2571,6 +2640,12 @@ class Dataframe(Changeable, IOComponent, JSONSerializable):
|
||||
self.max_rows = max_rows
|
||||
self.max_cols = max_cols
|
||||
self.overflow_row_behaviour = overflow_row_behaviour
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects cell within Dataframe.
|
||||
Uses event data gradio.SelectData to carry `value` referring to value of selected cell, and `index` tuple to refer to index row and column.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -2734,10 +2809,11 @@ class Dataframe(Changeable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
This method can be used to change the appearance of the DataFrame component.
|
||||
"""
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
def as_example(self, input_data: pd.DataFrame | np.ndarray | str | None):
|
||||
if input_data is None:
|
||||
@ -2883,10 +2959,11 @@ class Timeseries(Changeable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
This method can be used to change the appearance of the TimeSeries component.
|
||||
"""
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
def as_example(self, input_data: str | None) -> str:
|
||||
return Path(input_data).name if input_data else ""
|
||||
@ -3013,7 +3090,8 @@ class Button(Clickable, IOComponent, SimpleSerializable):
|
||||
if size is not None:
|
||||
self._style["size"] = size
|
||||
|
||||
return Component.style(self, **kwargs)
|
||||
Component.style(self, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
@ -3174,7 +3252,8 @@ class UploadButton(
|
||||
if size is not None:
|
||||
self._style["size"] = size
|
||||
|
||||
return Component.style(self, **kwargs)
|
||||
Component.style(self, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
@ -3285,7 +3364,7 @@ class ColorPicker(Changeable, Submittable, IOComponent, SimpleSerializable):
|
||||
|
||||
|
||||
@document("style")
|
||||
class Label(Changeable, IOComponent, JSONSerializable):
|
||||
class Label(Changeable, Selectable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
Displays a classification label, along with confidence scores of top categories, if provided.
|
||||
Preprocessing: this component does *not* accept input.
|
||||
@ -3323,6 +3402,12 @@ class Label(Changeable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
self.num_top_classes = num_top_classes
|
||||
self.color = color
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects a category from Label.
|
||||
Uses event data gradio.SelectData to carry `value` referring to name of selected category, and `index` to refer to index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -3339,6 +3424,7 @@ class Label(Changeable, IOComponent, JSONSerializable):
|
||||
"num_top_classes": self.num_top_classes,
|
||||
"value": self.value,
|
||||
"color": self.color,
|
||||
"selectable": self.selectable,
|
||||
**IOComponent.get_config(self),
|
||||
}
|
||||
|
||||
@ -3416,11 +3502,12 @@ class Label(Changeable, IOComponent, JSONSerializable):
|
||||
Parameters:
|
||||
container: If True, will add a container to the label - providing some extra padding around the border.
|
||||
"""
|
||||
return Component.style(self, container=container)
|
||||
Component.style(self, container=container)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
class HighlightedText(Changeable, IOComponent, JSONSerializable):
|
||||
class HighlightedText(Changeable, Selectable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
Displays text that contains spans that are highlighted by category or numerical value.
|
||||
Preprocessing: this component does *not* accept input.
|
||||
@ -3466,6 +3553,12 @@ class HighlightedText(Changeable, IOComponent, JSONSerializable):
|
||||
self.show_legend = show_legend
|
||||
self.combine_adjacent = combine_adjacent
|
||||
self.adjacent_separator = adjacent_separator
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects Highlighted text span.
|
||||
Uses event data gradio.SelectData to carry `value` referring to selected [text, label] tuple, and `index` to refer to span index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -3482,6 +3575,7 @@ class HighlightedText(Changeable, IOComponent, JSONSerializable):
|
||||
"color_map": self.color_map,
|
||||
"show_legend": self.show_legend,
|
||||
"value": self.value,
|
||||
"selectable": self.selectable,
|
||||
**IOComponent.get_config(self),
|
||||
}
|
||||
|
||||
@ -3580,7 +3674,8 @@ class HighlightedText(Changeable, IOComponent, JSONSerializable):
|
||||
if color_map is not None:
|
||||
self._style["color_map"] = color_map
|
||||
|
||||
return Component.style(self, container=container, **kwargs)
|
||||
Component.style(self, container=container, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
@ -3666,7 +3761,8 @@ class JSON(Changeable, IOComponent, JSONSerializable):
|
||||
Parameters:
|
||||
container: If True, will place the JSON in a container - providing some extra padding around the border.
|
||||
"""
|
||||
return Component.style(self, container=container, **kwargs)
|
||||
Component.style(self, container=container, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document()
|
||||
@ -3738,7 +3834,7 @@ class HTML(Changeable, IOComponent, SimpleSerializable):
|
||||
|
||||
|
||||
@document("style")
|
||||
class Gallery(IOComponent, TempFileManager, FileSerializable):
|
||||
class Gallery(IOComponent, TempFileManager, FileSerializable, Selectable):
|
||||
"""
|
||||
Used to display a list of images as a gallery that can be scrolled through.
|
||||
Preprocessing: this component does *not* accept input.
|
||||
@ -3767,8 +3863,15 @@ class Gallery(IOComponent, TempFileManager, FileSerializable):
|
||||
visible: If False, component will be hidden.
|
||||
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
"""
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects image within Gallery.
|
||||
Uses event data gradio.SelectData to carry `value` referring to caption of selected image, and `index` to refer to index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
TempFileManager.__init__(self)
|
||||
super().__init__(
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
every=every,
|
||||
show_label=show_label,
|
||||
@ -3859,6 +3962,7 @@ class Gallery(IOComponent, TempFileManager, FileSerializable):
|
||||
grid: Represents the number of images that should be shown in one row, for each of the six standard screen sizes (<576px, <768px, <992px, <1200px, <1400px, >1400px). if fewer that 6 are given then the last will be used for all subsequent breakpoints
|
||||
height: Height of the gallery.
|
||||
container: If True, will place gallery in a container - providing some extra padding around the border.
|
||||
preview: If True, will display the Gallery in preview mode, which shows all of the images as thumbnails and allows the user to click on them to view them in full size.
|
||||
"""
|
||||
if grid is not None:
|
||||
self._style["grid"] = grid
|
||||
@ -3867,7 +3971,8 @@ class Gallery(IOComponent, TempFileManager, FileSerializable):
|
||||
if preview is not None:
|
||||
self._style["preview"] = preview
|
||||
|
||||
return Component.style(self, container=container, **kwargs)
|
||||
Component.style(self, container=container, **kwargs)
|
||||
return self
|
||||
|
||||
def deserialize(
|
||||
self,
|
||||
@ -3922,7 +4027,7 @@ class Carousel(IOComponent, Changeable, SimpleSerializable):
|
||||
|
||||
|
||||
@document("style")
|
||||
class Chatbot(Changeable, IOComponent, JSONSerializable):
|
||||
class Chatbot(Changeable, Selectable, IOComponent, JSONSerializable):
|
||||
"""
|
||||
Displays a chatbot output showing both user submitted messages and responses. Supports a subset of Markdown including bold, italics, code, and images.
|
||||
Preprocessing: this component does *not* accept input.
|
||||
@ -3957,6 +4062,13 @@ class Chatbot(Changeable, IOComponent, JSONSerializable):
|
||||
"The 'color_map' parameter has been deprecated.",
|
||||
)
|
||||
self.md = utils.get_markdown_parser()
|
||||
self.select: EventListenerMethod
|
||||
"""
|
||||
Event listener for when the user selects message from Chatbot.
|
||||
Uses event data gradio.SelectData to carry `value` referring to text of selected message, and `index` tuple to refer to [message, participant] index.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
|
||||
IOComponent.__init__(
|
||||
self,
|
||||
label=label,
|
||||
@ -3971,6 +4083,7 @@ class Chatbot(Changeable, IOComponent, JSONSerializable):
|
||||
def get_config(self):
|
||||
return {
|
||||
"value": self.value,
|
||||
"selectable": self.selectable,
|
||||
**IOComponent.get_config(self),
|
||||
}
|
||||
|
||||
@ -4052,10 +4165,11 @@ class Chatbot(Changeable, IOComponent, JSONSerializable):
|
||||
if kwargs.get("color_map") is not None:
|
||||
warnings.warn("The 'color_map' parameter has been deprecated.")
|
||||
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
@document("style")
|
||||
@ -4173,10 +4287,11 @@ class Model3D(
|
||||
"""
|
||||
This method can be used to change the appearance of the Model3D component.
|
||||
"""
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
**kwargs,
|
||||
)
|
||||
return self
|
||||
|
||||
def as_example(self, input_data: str | None) -> str:
|
||||
return Path(input_data).name if input_data else ""
|
||||
@ -4280,10 +4395,11 @@ class Plot(Changeable, Clearable, IOComponent, JSONSerializable):
|
||||
return {"type": dtype, "plot": out_y}
|
||||
|
||||
def style(self, container: bool | None = None):
|
||||
return Component.style(
|
||||
Component.style(
|
||||
self,
|
||||
container=container,
|
||||
)
|
||||
return self
|
||||
|
||||
|
||||
class AltairPlot:
|
||||
@ -5465,7 +5581,7 @@ class Code(Changeable, IOComponent, SimpleSerializable):
|
||||
|
||||
|
||||
@document("style")
|
||||
class Dataset(Clickable, Component):
|
||||
class Dataset(Clickable, Selectable, Component):
|
||||
"""
|
||||
Used to create an output widget for showing datasets. Used to render the examples
|
||||
box.
|
||||
@ -5564,7 +5680,8 @@ class Dataset(Clickable, Component):
|
||||
"""
|
||||
This method can be used to change the appearance of the Dataset component.
|
||||
"""
|
||||
return Component.style(self, **kwargs)
|
||||
Component.style(self, **kwargs)
|
||||
return self
|
||||
|
||||
|
||||
@document()
|
||||
|
@ -10,6 +10,7 @@ class PredictBody(BaseModel):
|
||||
session_hash: Optional[str]
|
||||
event_id: Optional[str]
|
||||
data: List[Any]
|
||||
event_data: Optional[Any]
|
||||
fn_index: Optional[int]
|
||||
batched: Optional[
|
||||
bool
|
||||
|
@ -20,15 +20,22 @@ def set_documentation_group(m):
|
||||
def extract_instance_attr_doc(cls, attr):
|
||||
code = inspect.getsource(cls.__init__)
|
||||
lines = [line.strip() for line in code.split("\n")]
|
||||
found_attr = False
|
||||
i = 0
|
||||
i = None
|
||||
for i, line in enumerate(lines):
|
||||
if line.startswith("self." + attr):
|
||||
found_attr = True
|
||||
if line.startswith("self." + attr + ":") or line.startswith(
|
||||
"self." + attr + " ="
|
||||
):
|
||||
break
|
||||
assert found_attr, f"Could not find {attr} in {cls.__name__}"
|
||||
assert i is not None, f"Could not find {attr} in {cls.__name__}"
|
||||
start_line = lines.index('"""', i)
|
||||
end_line = lines.index('"""', start_line + 1)
|
||||
for j in range(i + 1, start_line):
|
||||
assert not lines[j].startswith("self."), (
|
||||
f"Found another attribute before docstring for {attr} in {cls.__name__}: "
|
||||
+ lines[j]
|
||||
+ "\n start:"
|
||||
+ lines[i]
|
||||
)
|
||||
doc_string = " ".join(lines[start_line + 1 : end_line])
|
||||
return doc_string
|
||||
|
||||
@ -242,7 +249,13 @@ def generate_documentation():
|
||||
and issubclass(cls, super_class)
|
||||
and cls != super_class
|
||||
):
|
||||
documentation[mode][i]["fns"] += classes_inherit_documentation[
|
||||
super_class
|
||||
]
|
||||
for inherited_fn in classes_inherit_documentation[super_class]:
|
||||
inherited_fn = dict(inherited_fn)
|
||||
try:
|
||||
inherited_fn["description"] = extract_instance_attr_doc(
|
||||
cls, inherited_fn["name"]
|
||||
)
|
||||
except (ValueError, AssertionError):
|
||||
pass
|
||||
documentation[mode][i]["fns"].append(inherited_fn)
|
||||
return documentation
|
||||
|
@ -4,10 +4,11 @@ of the on-page-load event, which is defined in gr.Blocks().load()."""
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Set
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Set, Tuple
|
||||
|
||||
from gradio.blocks import Block
|
||||
from gradio.documentation import document, set_documentation_group
|
||||
from gradio.helpers import EventData
|
||||
from gradio.utils import get_cancel_function
|
||||
|
||||
if TYPE_CHECKING: # Only import for type checking (is False at runtime).
|
||||
@ -264,3 +265,34 @@ class Releaseable(EventListener):
|
||||
"""
|
||||
This event is triggered when the user releases the mouse on this component (e.g. when the user releases the slider). This method can be used when this component is in a Gradio Blocks.
|
||||
"""
|
||||
|
||||
|
||||
@document("*select", inherit=True)
|
||||
class Selectable(EventListener):
|
||||
def __init__(self):
|
||||
self.selectable: bool = False
|
||||
self.select = EventListenerMethod(
|
||||
self, "select", callback=lambda: setattr(self, "selectable", True)
|
||||
)
|
||||
"""
|
||||
This event is triggered when the user selects from within the Component.
|
||||
This event has EventData of type gradio.SelectData that carries information, accessible through SelectData.index and SelectData.value.
|
||||
See EventData documentation on how to use this event data.
|
||||
"""
|
||||
|
||||
|
||||
class SelectData(EventData):
|
||||
def __init__(self, target: Block | None, data: Any):
|
||||
super().__init__(target, data)
|
||||
self.index: int | Tuple[int, int] = data["index"]
|
||||
"""
|
||||
The index of the selected item. Is a tuple if the component is two dimensional or selection is a range.
|
||||
"""
|
||||
self.value: Any = data["value"]
|
||||
"""
|
||||
The value of the selected item.
|
||||
"""
|
||||
self.selected: bool = data.get("selected", True)
|
||||
"""
|
||||
True if the item was selected, False if deselected.
|
||||
"""
|
||||
|
@ -26,6 +26,7 @@ from gradio.documentation import document, set_documentation_group
|
||||
from gradio.flagging import CSVLogger
|
||||
|
||||
if TYPE_CHECKING: # Only import for type checking (to avoid circular imports).
|
||||
from gradio.blocks import Block
|
||||
from gradio.components import IOComponent
|
||||
|
||||
CACHED_FOLDER = "gradio_cached_examples"
|
||||
@ -594,16 +595,17 @@ def special_args(
|
||||
fn: Callable,
|
||||
inputs: List[Any] | None = None,
|
||||
request: routes.Request | None = None,
|
||||
event_data: EventData | None = None,
|
||||
):
|
||||
"""
|
||||
Checks if function has special arguments Request (via annotation) or Progress (via default value).
|
||||
Checks if function has special arguments Request or EventData (via annotation) or Progress (via default value).
|
||||
If inputs is provided, these values will be loaded into the inputs array.
|
||||
Parameters:
|
||||
block_fn: function to check.
|
||||
inputs: array to load special arguments into.
|
||||
request: request to load into inputs.
|
||||
Returns:
|
||||
updated inputs, request index, progress index
|
||||
updated inputs, progress index, event data index.
|
||||
"""
|
||||
signature = inspect.signature(fn)
|
||||
positional_args = []
|
||||
@ -612,6 +614,7 @@ def special_args(
|
||||
break
|
||||
positional_args.append(param)
|
||||
progress_index = None
|
||||
event_data_index = None
|
||||
for i, param in enumerate(positional_args):
|
||||
if isinstance(param.default, Progress):
|
||||
progress_index = i
|
||||
@ -620,6 +623,12 @@ def special_args(
|
||||
elif param.annotation == routes.Request:
|
||||
if inputs is not None:
|
||||
inputs.insert(i, request)
|
||||
elif isinstance(param.annotation, type) and issubclass(
|
||||
param.annotation, EventData
|
||||
):
|
||||
event_data_index = i
|
||||
if inputs is not None and event_data is not None:
|
||||
inputs.insert(i, param.annotation(event_data.target, event_data._data))
|
||||
if inputs is not None:
|
||||
while len(inputs) < len(positional_args):
|
||||
i = len(inputs)
|
||||
@ -629,7 +638,7 @@ def special_args(
|
||||
inputs.append(None)
|
||||
else:
|
||||
inputs.append(param.default)
|
||||
return inputs or [], progress_index
|
||||
return inputs or [], progress_index, event_data_index
|
||||
|
||||
|
||||
@document()
|
||||
@ -797,3 +806,34 @@ def make_waveform(
|
||||
|
||||
subprocess.call(ffmpeg_cmd, shell=True)
|
||||
return output_mp4.name
|
||||
|
||||
|
||||
@document()
|
||||
class EventData:
|
||||
"""
|
||||
When a subclass of EventData is added as a type hint to an argument of an event listener method, this object will be passed as that argument.
|
||||
It contains information about the event that triggered the listener, such the target object, and other data related to the specific event that are attributes of the subclass.
|
||||
|
||||
Example:
|
||||
table = gr.Dataframe([[1, 2, 3], [4, 5, 6]])
|
||||
gallery = gr.Gallery([("cat.jpg", "Cat"), ("dog.jpg", "Dog")])
|
||||
textbox = gr.Textbox("Hello World!")
|
||||
|
||||
statement = gr.Textbox()
|
||||
|
||||
def on_select(evt: gr.SelectData): # SelectData is a subclass of EventData
|
||||
return f"You selected {evt.value} at {evt.index} from {evt.target}"
|
||||
|
||||
table.select(on_select, None, statement)
|
||||
gallery.select(on_select, None, statement)
|
||||
textbox.select(on_select, None, statement)
|
||||
Demos: gallery_selections, tictactoe
|
||||
"""
|
||||
|
||||
def __init__(self, target: Block | None, _data: Any):
|
||||
"""
|
||||
Parameters:
|
||||
target: The target object that triggered the event. Can be used to distinguish if multiple components are bound to the same listener.
|
||||
"""
|
||||
self.target = target
|
||||
self._data = _data
|
||||
|
@ -1,16 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import warnings
|
||||
from typing import TYPE_CHECKING, Callable, List, Type
|
||||
from typing import Type
|
||||
|
||||
from gradio.blocks import BlockContext
|
||||
from gradio.documentation import document, set_documentation_group
|
||||
from gradio.events import Changeable, Selectable
|
||||
|
||||
set_documentation_group("layout")
|
||||
|
||||
if TYPE_CHECKING: # Only import for type checking (is False at runtime).
|
||||
from gradio.components import Component
|
||||
|
||||
|
||||
@document()
|
||||
class Row(BlockContext):
|
||||
@ -139,7 +137,7 @@ class Column(BlockContext):
|
||||
}
|
||||
|
||||
|
||||
class Tabs(BlockContext):
|
||||
class Tabs(BlockContext, Changeable, Selectable):
|
||||
"""
|
||||
Tabs is a layout element within Blocks that can contain multiple "Tab" Components.
|
||||
"""
|
||||
@ -158,11 +156,13 @@ class Tabs(BlockContext):
|
||||
visible: If False, Tabs will be hidden.
|
||||
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
"""
|
||||
super().__init__(visible=visible, elem_id=elem_id, **kwargs)
|
||||
BlockContext.__init__(self, visible=visible, elem_id=elem_id, **kwargs)
|
||||
Changeable.__init__(self)
|
||||
Selectable.__init__(self)
|
||||
self.selected = selected
|
||||
|
||||
def get_config(self):
|
||||
return {"selected": self.selected, **super().get_config()}
|
||||
return {"selected": self.selected, **super(BlockContext, self).get_config()}
|
||||
|
||||
@staticmethod
|
||||
def update(
|
||||
@ -173,19 +173,9 @@ class Tabs(BlockContext):
|
||||
"__type__": "update",
|
||||
}
|
||||
|
||||
def change(self, fn: Callable, inputs: List[Component], outputs: List[Component]):
|
||||
"""
|
||||
Parameters:
|
||||
fn: Callable function
|
||||
inputs: List of inputs
|
||||
outputs: List of outputs
|
||||
Returns: None
|
||||
"""
|
||||
self.set_event_trigger("change", fn, inputs, outputs)
|
||||
|
||||
|
||||
@document()
|
||||
class Tab(BlockContext):
|
||||
class Tab(BlockContext, Selectable):
|
||||
"""
|
||||
Tab (or its alias TabItem) is a layout element. Components defined within the Tab will be visible when this tab is selected tab.
|
||||
Example:
|
||||
@ -213,7 +203,8 @@ class Tab(BlockContext):
|
||||
id: An optional identifier for the tab, required if you wish to control the selected tab from a predict function.
|
||||
elem_id: An optional string that is assigned as the id of this component in the HTML DOM. Can be used for targeting CSS styles.
|
||||
"""
|
||||
super().__init__(elem_id=elem_id, **kwargs)
|
||||
BlockContext.__init__(self, elem_id=elem_id, **kwargs)
|
||||
Selectable.__init__(self)
|
||||
self.label = label
|
||||
self.id = id
|
||||
|
||||
@ -221,19 +212,9 @@ class Tab(BlockContext):
|
||||
return {
|
||||
"label": self.label,
|
||||
"id": self.id,
|
||||
**super().get_config(),
|
||||
**super(BlockContext, self).get_config(),
|
||||
}
|
||||
|
||||
def select(self, fn: Callable, inputs: List[Component], outputs: List[Component]):
|
||||
"""
|
||||
Parameters:
|
||||
fn: Callable function
|
||||
inputs: List of inputs
|
||||
outputs: List of outputs
|
||||
Returns: None
|
||||
"""
|
||||
self.set_event_trigger("select", fn, inputs, outputs)
|
||||
|
||||
def get_expected_parent(self) -> Type[Tabs]:
|
||||
return Tabs
|
||||
|
||||
|
@ -45,6 +45,7 @@ from gradio.context import Context
|
||||
from gradio.data_classes import PredictBody, ResetBody
|
||||
from gradio.documentation import document, set_documentation_group
|
||||
from gradio.exceptions import Error
|
||||
from gradio.helpers import EventData
|
||||
from gradio.processing_utils import TempFileManager
|
||||
from gradio.queueing import Estimation, Event
|
||||
from gradio.utils import cancel_tasks, run_coro_in_background, set_task_name
|
||||
@ -378,7 +379,14 @@ class App(FastAPI):
|
||||
event_id = getattr(body, "event_id", None)
|
||||
raw_input = body.data
|
||||
fn_index = body.fn_index
|
||||
batch = app.get_blocks().dependencies[fn_index_inferred]["batch"]
|
||||
|
||||
dependency = app.get_blocks().dependencies[fn_index_inferred]
|
||||
target = dependency["targets"][0] if len(dependency["targets"]) else None
|
||||
event_data = EventData(
|
||||
app.get_blocks().blocks[target] if target else None,
|
||||
body.event_data,
|
||||
)
|
||||
batch = dependency["batch"]
|
||||
if not (body.batched) and batch:
|
||||
raw_input = [raw_input]
|
||||
try:
|
||||
@ -389,6 +397,7 @@ class App(FastAPI):
|
||||
state=session_state,
|
||||
iterators=iterators,
|
||||
event_id=event_id,
|
||||
event_data=event_data,
|
||||
)
|
||||
iterator = output.pop("iterator", None)
|
||||
if hasattr(body, "session_hash"):
|
||||
|
@ -196,6 +196,7 @@ XRAY_CONFIG = {
|
||||
"max_batch_size": 4,
|
||||
"cancels": [],
|
||||
"every": None,
|
||||
"collects_event_data": False,
|
||||
"types": {"continuous": False, "generator": False},
|
||||
"trigger_after": None,
|
||||
"trigger_only_on_success": False,
|
||||
@ -215,6 +216,7 @@ XRAY_CONFIG = {
|
||||
"max_batch_size": 4,
|
||||
"cancels": [],
|
||||
"every": None,
|
||||
"collects_event_data": False,
|
||||
"types": {"continuous": False, "generator": False},
|
||||
"trigger_after": None,
|
||||
"trigger_only_on_success": False,
|
||||
@ -234,6 +236,7 @@ XRAY_CONFIG = {
|
||||
"max_batch_size": 4,
|
||||
"cancels": [],
|
||||
"every": None,
|
||||
"collects_event_data": False,
|
||||
"types": {"continuous": False, "generator": False},
|
||||
"trigger_after": None,
|
||||
"trigger_only_on_success": False,
|
||||
@ -440,6 +443,7 @@ XRAY_CONFIG_DIFF_IDS = {
|
||||
"max_batch_size": 4,
|
||||
"cancels": [],
|
||||
"every": None,
|
||||
"collects_event_data": False,
|
||||
"types": {"continuous": False, "generator": False},
|
||||
"trigger_after": None,
|
||||
"trigger_only_on_success": False,
|
||||
@ -459,6 +463,7 @@ XRAY_CONFIG_DIFF_IDS = {
|
||||
"max_batch_size": 4,
|
||||
"cancels": [],
|
||||
"every": None,
|
||||
"collects_event_data": False,
|
||||
"types": {"continuous": False, "generator": False},
|
||||
"trigger_after": None,
|
||||
"trigger_only_on_success": False,
|
||||
@ -478,6 +483,7 @@ XRAY_CONFIG_DIFF_IDS = {
|
||||
"max_batch_size": 4,
|
||||
"cancels": [],
|
||||
"every": None,
|
||||
"collects_event_data": False,
|
||||
"types": {"continuous": False, "generator": False},
|
||||
"trigger_after": None,
|
||||
"trigger_only_on_success": False,
|
||||
|
@ -920,10 +920,14 @@ def check_function_inputs_match(fn: Callable, inputs: List, inputs_as_dict: bool
|
||||
"""
|
||||
|
||||
def is_special_typed_parameter(name):
|
||||
from gradio.helpers import EventData
|
||||
from gradio.routes import Request
|
||||
|
||||
"""Checks if parameter has a type hint designating it as a gr.Request"""
|
||||
return parameter_types.get(name, "") == Request
|
||||
"""Checks if parameter has a type hint designating it as a gr.Request or gr.EventData"""
|
||||
is_request = parameter_types.get(name, "") == Request
|
||||
# use int in the fall-back as that will always be false
|
||||
is_event_data = issubclass(parameter_types.get(name, int), EventData)
|
||||
return is_request or is_event_data
|
||||
|
||||
signature = inspect.signature(fn)
|
||||
parameter_types = typing.get_type_hints(fn) if inspect.isfunction(fn) else {}
|
||||
@ -933,7 +937,7 @@ def check_function_inputs_match(fn: Callable, inputs: List, inputs_as_dict: bool
|
||||
for name, param in signature.parameters.items():
|
||||
has_default = param.default != param.empty
|
||||
if param.kind in [param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD]:
|
||||
if not (is_special_typed_parameter(name)):
|
||||
if not is_special_typed_parameter(name):
|
||||
if not has_default:
|
||||
min_args += 1
|
||||
max_args += 1
|
||||
|
@ -145,3 +145,14 @@ Here is an example of a sine curve that updates every second!
|
||||
|
||||
$code_sine_curve
|
||||
$demo_sine_curve
|
||||
|
||||
## Gathering Event Data
|
||||
|
||||
You can gather specific data about an event by adding the associated event data class as a type hint to an argument in the event listener function.
|
||||
|
||||
For example, event data for `.select()` can be type hinted by a `gradio.SelectData` argument. This event is triggered when a user selects some part of the triggering component, and the event data includes information about what the user specifically selected. If a user selected a specific word in a `Textbox`, a specific image in a `Gallery`, or a specific cell in a `DataFrame`, the event data argument would contain information about the specific selection.
|
||||
|
||||
In the 2 player tic-tac-toe demo below, a user can select a cell in the `DataFrame` to make a move. The event data argument contains information about the specific cell that was selected. We can first check to see if the cell is empty, and then update the cell with the user's move.
|
||||
|
||||
$code_tictactoe
|
||||
$demo_tictactoe
|
@ -938,6 +938,7 @@ class TestFile:
|
||||
"value": None,
|
||||
"interactive": None,
|
||||
"root_url": None,
|
||||
"selectable": False,
|
||||
}
|
||||
assert file_input.preprocess(None) is None
|
||||
x_file["is_example"] = True
|
||||
@ -1506,6 +1507,7 @@ class TestLabel:
|
||||
"interactive": None,
|
||||
"root_url": None,
|
||||
"color": None,
|
||||
"selectable": False,
|
||||
}
|
||||
|
||||
def test_color_argument(self):
|
||||
@ -1644,6 +1646,7 @@ class TestHighlightedText:
|
||||
"value": None,
|
||||
"interactive": None,
|
||||
"root_url": None,
|
||||
"selectable": False,
|
||||
}
|
||||
|
||||
def test_in_interface(self):
|
||||
@ -1737,6 +1740,7 @@ class TestChatbot:
|
||||
"elem_id": None,
|
||||
"style": {},
|
||||
"root_url": None,
|
||||
"selectable": False,
|
||||
}
|
||||
|
||||
|
||||
@ -2568,7 +2572,7 @@ class TestCode:
|
||||
path = str(Path(test_file_dir, "test_label_json.json"))
|
||||
with open(path) as f:
|
||||
assert code.postprocess(path) == path
|
||||
assert code.postprocess((path, )) == f.read()
|
||||
assert code.postprocess((path,)) == f.read()
|
||||
|
||||
assert code.serialize("def fn(a):\n return a") == "def fn(a):\n return a"
|
||||
assert code.deserialize("def fn(a):\n return a") == "def fn(a):\n return a"
|
||||
|
@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
import gradio as gr
|
||||
|
||||
@ -17,6 +18,26 @@ class TestEvent:
|
||||
|
||||
assert demo.config["dependencies"][0]["trigger"] == "clear"
|
||||
|
||||
def test_event_data(self):
|
||||
with gr.Blocks() as demo:
|
||||
text = gr.Textbox()
|
||||
gallery = gr.Gallery()
|
||||
|
||||
def fn_img_index(evt: gr.SelectData):
|
||||
return evt.index
|
||||
|
||||
gallery.select(fn_img_index, None, text)
|
||||
|
||||
app, _, _ = demo.launch(prevent_thread_lock=True)
|
||||
client = TestClient(app)
|
||||
|
||||
resp = client.post(
|
||||
f"{demo.local_url}run/predict",
|
||||
json={"fn_index": 0, "data": [], "event_data": {"index": 1, "value": None}},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["data"][0] == "1"
|
||||
|
||||
def test_consecutive_events(self):
|
||||
def double(x):
|
||||
return x + x
|
||||
|
@ -240,7 +240,7 @@
|
||||
dep.trigger_after === fn_index &&
|
||||
(!dep.trigger_only_on_success || status === "complete")
|
||||
) {
|
||||
trigger_api_call(i);
|
||||
trigger_api_call(i, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -259,7 +259,7 @@
|
||||
}
|
||||
let handled_dependencies: Array<number[]> = [];
|
||||
|
||||
const trigger_api_call = (dep_index: number) => {
|
||||
const trigger_api_call = (dep_index: number, event_data: unknown) => {
|
||||
let dep = dependencies[dep_index];
|
||||
const current_status = loading_status.get_status_for_fn(dep_index);
|
||||
if (current_status === "pending" || current_status === "generating") {
|
||||
@ -274,7 +274,8 @@
|
||||
|
||||
let payload = {
|
||||
fn_index: dep_index,
|
||||
data: dep.inputs.map((id) => instance_map[id].props.value)
|
||||
data: dep.inputs.map((id) => instance_map[id].props.value),
|
||||
event_data: dep.collects_event_data ? event_data : null
|
||||
};
|
||||
|
||||
if (dep.frontend_fn) {
|
||||
@ -329,7 +330,7 @@
|
||||
outputs.every((v) => instance_map?.[v].instance) &&
|
||||
inputs.every((v) => instance_map?.[v].instance)
|
||||
) {
|
||||
trigger_api_call(i);
|
||||
trigger_api_call(i, null);
|
||||
handled_dependencies[i] = [-1];
|
||||
}
|
||||
|
||||
@ -337,8 +338,8 @@
|
||||
.filter((v) => !!v && !!v[1])
|
||||
.forEach(([id, { instance }]: [number, ComponentMeta]) => {
|
||||
if (handled_dependencies[i]?.includes(id) || !instance) return;
|
||||
instance?.$on(trigger, () => {
|
||||
trigger_api_call(i);
|
||||
instance?.$on(trigger, (event_data) => {
|
||||
trigger_api_call(i, event_data.detail);
|
||||
});
|
||||
|
||||
if (!handled_dependencies[i]) handled_dependencies[i] = [];
|
||||
|
@ -18,6 +18,7 @@
|
||||
export let show_label: boolean = true;
|
||||
export let root: string;
|
||||
export let root_url: null | string;
|
||||
export let selectable: boolean = false;
|
||||
|
||||
const redirect_src_url = (src: string) =>
|
||||
src.replace('src="/file', `src="${root}file`);
|
||||
@ -47,8 +48,10 @@
|
||||
{/if}
|
||||
<ChatBot
|
||||
{style}
|
||||
{selectable}
|
||||
value={_value}
|
||||
pending_message={loading_status?.status === "pending"}
|
||||
on:change
|
||||
on:select
|
||||
/>
|
||||
</Block>
|
||||
|
@ -25,5 +25,11 @@
|
||||
{#if info}
|
||||
<Info>{info}</Info>
|
||||
{/if}
|
||||
<Checkbox {label} bind:value on:change disabled={mode === "static"} />
|
||||
<Checkbox
|
||||
{label}
|
||||
bind:value
|
||||
on:change
|
||||
on:select
|
||||
disabled={mode === "static"}
|
||||
/>
|
||||
</Block>
|
||||
|
@ -33,6 +33,7 @@
|
||||
{info}
|
||||
{style}
|
||||
{show_label}
|
||||
on:select
|
||||
on:change
|
||||
disabled={mode === "static"}
|
||||
/>
|
||||
|
@ -45,6 +45,7 @@
|
||||
values={value}
|
||||
{headers}
|
||||
on:change={({ detail }) => handle_change(detail)}
|
||||
on:select
|
||||
editable={mode === "dynamic"}
|
||||
{wrap}
|
||||
{datatype}
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import type { SvelteComponentDev, ComponentType } from "svelte/internal";
|
||||
import { component_map } from "./directory";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
export let components: Array<keyof typeof component_map>;
|
||||
export let label: string = "Examples";
|
||||
@ -15,7 +16,10 @@
|
||||
export let root_url: null | string;
|
||||
export let samples_per_page: number = 10;
|
||||
|
||||
const dispatch = createEventDispatcher<{ click: number }>();
|
||||
const dispatch = createEventDispatcher<{
|
||||
click: number;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
let samples_dir: string = root_url
|
||||
? "proxy=" + root_url + "/file="
|
||||
@ -100,6 +104,7 @@
|
||||
on:click={() => {
|
||||
value = i + page * samples_per_page;
|
||||
dispatch("click", value);
|
||||
dispatch("select", { index: value, value: sample_row });
|
||||
}}
|
||||
on:mouseenter={() => handle_mouseenter(i)}
|
||||
on:mouseleave={() => handle_mouseleave()}
|
||||
|
@ -42,6 +42,7 @@
|
||||
{info}
|
||||
{show_label}
|
||||
on:change
|
||||
on:select
|
||||
disabled={mode === "static"}
|
||||
/>
|
||||
</Block>
|
||||
|
@ -24,6 +24,7 @@
|
||||
export let file_count: string;
|
||||
export let file_types: Array<string> = ["file"];
|
||||
export let root_url: null | string;
|
||||
export let selectable: boolean = false;
|
||||
|
||||
export let loading_status: LoadingStatus;
|
||||
|
||||
@ -108,14 +109,16 @@
|
||||
value={_value}
|
||||
{file_count}
|
||||
{file_types}
|
||||
{selectable}
|
||||
on:change={({ detail }) => (value = detail)}
|
||||
on:drag={({ detail }) => (dragging = detail)}
|
||||
on:clear
|
||||
on:upload
|
||||
on:select
|
||||
>
|
||||
<UploadText type="file" />
|
||||
</FileUpload>
|
||||
{:else}
|
||||
<FileComponent value={_value} {label} {show_label} />
|
||||
<FileComponent on:select {selectable} value={_value} {label} {show_label} />
|
||||
{/if}
|
||||
</Block>
|
||||
|
@ -25,5 +25,5 @@
|
||||
disable={typeof style.container === "boolean" && !style.container}
|
||||
>
|
||||
<StatusTracker {...loading_status} />
|
||||
<Gallery {label} {value} {style} {show_label} {root} {root_url} />
|
||||
<Gallery on:select {label} {value} {style} {show_label} {root} {root_url} />
|
||||
</Block>
|
||||
|
@ -13,8 +13,9 @@
|
||||
let old_value: Array<[string, string | number]>;
|
||||
export let show_legend: boolean;
|
||||
export let color_map: Record<string, string> = {};
|
||||
export let label: string;
|
||||
export let label: string = "Highlighted Text";
|
||||
export let style: Styles = {};
|
||||
export let selectable: boolean = false;
|
||||
|
||||
$: if (!style.color_map && Object.keys(color_map).length) {
|
||||
style.color_map = color_map;
|
||||
@ -36,6 +37,7 @@
|
||||
test_id="highlighted-text"
|
||||
{visible}
|
||||
{elem_id}
|
||||
padding={false}
|
||||
disable={typeof style.container === "boolean" && !style.container}
|
||||
>
|
||||
<StatusTracker {...loading_status} />
|
||||
@ -43,12 +45,19 @@
|
||||
<BlockLabel
|
||||
Icon={TextHighlight}
|
||||
{label}
|
||||
float={false}
|
||||
disable={typeof style.container === "boolean" && !style.container}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if value}
|
||||
<HighlightedText {value} {show_legend} color_map={style.color_map} />
|
||||
<HighlightedText
|
||||
on:select
|
||||
{selectable}
|
||||
{value}
|
||||
{show_legend}
|
||||
color_map={style.color_map}
|
||||
/>
|
||||
{:else}
|
||||
<Empty>
|
||||
<TextHighlight />
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
export let loading_status: LoadingStatus;
|
||||
export let show_label: boolean;
|
||||
export let selectable: boolean = false;
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: undefined }>();
|
||||
|
||||
@ -40,7 +41,7 @@
|
||||
/>
|
||||
{/if}
|
||||
{#if typeof value === "object" && value !== undefined && value !== null}
|
||||
<Label {value} {show_label} {color} />
|
||||
<Label on:select {selectable} {value} {show_label} {color} />
|
||||
{:else}
|
||||
<Empty><LabelIcon /></Empty>
|
||||
{/if}
|
||||
|
@ -9,7 +9,7 @@
|
||||
export let info: string | undefined = undefined;
|
||||
export let elem_id: string = "";
|
||||
export let visible: boolean = true;
|
||||
export let value: string = "";
|
||||
export let value: string | null = null;
|
||||
export let choices: Array<string> = [];
|
||||
export let mode: "static" | "dynamic";
|
||||
export let show_label: boolean;
|
||||
@ -35,5 +35,6 @@
|
||||
{style}
|
||||
disabled={mode === "static"}
|
||||
on:change
|
||||
on:select
|
||||
/>
|
||||
</Block>
|
||||
|
@ -11,6 +11,6 @@
|
||||
$: dispatch("prop_change", { selected });
|
||||
</script>
|
||||
|
||||
<Tabs {visible} {elem_id} bind:selected on:change>
|
||||
<Tabs {visible} {elem_id} bind:selected on:change on:select>
|
||||
<slot />
|
||||
</Tabs>
|
||||
|
@ -46,6 +46,7 @@
|
||||
on:change
|
||||
on:submit
|
||||
on:blur
|
||||
on:select
|
||||
disabled={mode === "static"}
|
||||
/>
|
||||
</Block>
|
||||
|
@ -37,6 +37,7 @@ export interface Dependency {
|
||||
api_name: string | null;
|
||||
cancels: Array<number>;
|
||||
types: DependencyTypes;
|
||||
collects_event_data: boolean;
|
||||
trigger_after?: number;
|
||||
trigger_only_on_success?: boolean;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { beforeUpdate, afterUpdate, createEventDispatcher } from "svelte";
|
||||
import type { Styles } from "@gradio/utils";
|
||||
import type { Styles, SelectData } from "@gradio/utils";
|
||||
import type { FileData } from "@gradio/upload";
|
||||
|
||||
export let value: Array<
|
||||
@ -12,11 +12,15 @@
|
||||
export let pending_message: boolean = false;
|
||||
export let feedback: Array<string> | null = null;
|
||||
export let style: Styles = {};
|
||||
export let selectable: boolean = false;
|
||||
|
||||
let div: HTMLDivElement;
|
||||
let autoscroll: Boolean;
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: undefined }>();
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: undefined;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
beforeUpdate(() => {
|
||||
autoscroll =
|
||||
@ -57,6 +61,9 @@
|
||||
class:latest={i === value.length - 1}
|
||||
class="message {j == 0 ? 'user' : 'bot'}"
|
||||
class:hide={message === null}
|
||||
class:selectable
|
||||
on:click={() =>
|
||||
dispatch("select", { index: [i, j], value: message })}
|
||||
>
|
||||
{#if typeof message === "string"}
|
||||
{@html message}
|
||||
@ -185,6 +192,9 @@
|
||||
.feedback button:hover {
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pending {
|
||||
display: flex;
|
||||
|
@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@gradio/atoms": "workspace:^0.0.1",
|
||||
"@gradio/icons": "workspace:^0.0.1",
|
||||
"@gradio/upload": "workspace:^0.0.1"
|
||||
"@gradio/upload": "workspace:^0.0.1",
|
||||
"@gradio/utils": "workspace:^0.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
export let value: FileData | FileData[] | null = null;
|
||||
export let label: string;
|
||||
export let show_label: boolean = true;
|
||||
export let selectable: boolean = false;
|
||||
</script>
|
||||
|
||||
<BlockLabel
|
||||
@ -17,7 +18,7 @@
|
||||
/>
|
||||
|
||||
{#if value}
|
||||
<FilePreview {value} />
|
||||
<FilePreview {selectable} on:select {value} />
|
||||
{:else}
|
||||
<Empty size="large" unpadded_box={true}><File /></Empty>
|
||||
{/if}
|
||||
|
@ -1,17 +1,29 @@
|
||||
<script lang="ts">
|
||||
import type { FileData } from "@gradio/upload";
|
||||
import { Download } from "@gradio/icons";
|
||||
import { IconButton } from "@gradio/atoms";
|
||||
import { display_file_name, display_file_size } from "./utils";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: SelectData;
|
||||
}>();
|
||||
export let value: FileData | FileData[];
|
||||
export let selectable: boolean = false;
|
||||
</script>
|
||||
|
||||
<div class="file-preview-holder">
|
||||
<table class="file-preview">
|
||||
<tbody>
|
||||
{#each Array.isArray(value) ? value : [value] as file}
|
||||
<tr class="file">
|
||||
{#each Array.isArray(value) ? value : [value] as file, i}
|
||||
<tr
|
||||
class="file"
|
||||
class:selectable
|
||||
on:click={() =>
|
||||
dispatch("select", {
|
||||
value: file.orig_name || file.name,
|
||||
index: i
|
||||
})}
|
||||
>
|
||||
<td>
|
||||
{display_file_name(file)}
|
||||
</td>
|
||||
@ -83,4 +95,7 @@
|
||||
.download > a:active {
|
||||
color: var(--text-color-link-active);
|
||||
}
|
||||
.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -13,6 +13,7 @@
|
||||
export let show_label: boolean = true;
|
||||
export let file_count: string = "single";
|
||||
export let file_types: string[] | null = null;
|
||||
export let selectable: boolean = false;
|
||||
|
||||
async function handle_upload({
|
||||
detail
|
||||
@ -64,7 +65,7 @@
|
||||
|
||||
{#if value}
|
||||
<ModifyUpload on:clear={handle_clear} absolute />
|
||||
<FilePreview {value} />
|
||||
<FilePreview on:select {selectable} {value} />
|
||||
{:else}
|
||||
<Upload
|
||||
on:load={handle_upload}
|
||||
|
@ -1,11 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
export let value: boolean;
|
||||
export let disabled: boolean = false;
|
||||
export let label: string;
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: boolean }>();
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: boolean;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
function handle_change(value: boolean) {
|
||||
dispatch("change", value);
|
||||
@ -18,6 +22,12 @@
|
||||
<label class:disabled>
|
||||
<input
|
||||
bind:checked={value}
|
||||
on:input={(evt) =>
|
||||
dispatch("select", {
|
||||
index: 0,
|
||||
value: label,
|
||||
selected: evt.currentTarget.checked
|
||||
})}
|
||||
{disabled}
|
||||
type="checkbox"
|
||||
name="test"
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { BlockTitle, Info } from "@gradio/atoms";
|
||||
import { get_styles } from "@gradio/utils";
|
||||
import type { Styles } from "@gradio/utils";
|
||||
import type { Styles, SelectData } from "@gradio/utils";
|
||||
|
||||
export let value: Array<string> = [];
|
||||
export let style: Styles = {};
|
||||
@ -12,7 +12,10 @@
|
||||
export let info: string | undefined = undefined;
|
||||
export let show_label: boolean;
|
||||
|
||||
const dispatch = createEventDispatcher<{ change: Array<string> }>();
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: Array<string>;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
const toggleChoice = (choice: string) => {
|
||||
if (value.includes(choice)) {
|
||||
@ -39,6 +42,12 @@
|
||||
<input
|
||||
{disabled}
|
||||
on:change={() => toggleChoice(choice)}
|
||||
on:input={(evt) =>
|
||||
dispatch("select", {
|
||||
index: choices.indexOf(choice),
|
||||
value: choice,
|
||||
selected: evt.currentTarget.checked
|
||||
})}
|
||||
checked={value.includes(choice)}
|
||||
type="checkbox"
|
||||
name="test"
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
<style>
|
||||
input {
|
||||
display: block;
|
||||
position: relative;
|
||||
background: var(--color-background-primary);
|
||||
line-height: var(--line-sm);
|
||||
|
@ -3,6 +3,7 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { BlockTitle } from "@gradio/atoms";
|
||||
import { Remove, DropdownArrow } from "@gradio/icons";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
export let label: string;
|
||||
export let info: string | undefined = undefined;
|
||||
export let value: string | Array<string> | undefined;
|
||||
@ -14,6 +15,7 @@
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: string | Array<string> | undefined;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
let inputValue: string,
|
||||
@ -43,6 +45,11 @@
|
||||
if (Array.isArray(value)) {
|
||||
if (!max_choices || value.length < max_choices) {
|
||||
value.push(option);
|
||||
dispatch("select", {
|
||||
index: choices.indexOf(option),
|
||||
value: option,
|
||||
selected: true
|
||||
});
|
||||
dispatch("change", value);
|
||||
}
|
||||
showOptions = !(value.length === max_choices);
|
||||
@ -53,6 +60,11 @@
|
||||
function remove(option: string) {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.filter((v: string) => v !== option);
|
||||
dispatch("select", {
|
||||
index: choices.indexOf(option),
|
||||
value: option,
|
||||
selected: false
|
||||
});
|
||||
dispatch("change", value);
|
||||
}
|
||||
}
|
||||
@ -78,6 +90,11 @@
|
||||
value = option;
|
||||
inputValue = "";
|
||||
showOptions = false;
|
||||
dispatch("select", {
|
||||
index: choices.indexOf(option),
|
||||
value: option,
|
||||
selected: true
|
||||
});
|
||||
dispatch("change", value);
|
||||
return;
|
||||
}
|
||||
|
@ -2,9 +2,9 @@
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { BlockTitle } from "@gradio/atoms";
|
||||
import { get_styles } from "@gradio/utils";
|
||||
import type { Styles } from "@gradio/utils";
|
||||
import type { Styles, SelectData } from "@gradio/utils";
|
||||
|
||||
export let value: string;
|
||||
export let value: string | null;
|
||||
export let style: Styles = {};
|
||||
export let choices: Array<string>;
|
||||
export let disabled: boolean = false;
|
||||
@ -13,7 +13,10 @@
|
||||
export let show_label: boolean = true;
|
||||
export let elem_id: string;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: string | null;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
$: dispatch("change", value);
|
||||
|
||||
@ -32,6 +35,7 @@
|
||||
<input
|
||||
{disabled}
|
||||
bind:group={value}
|
||||
on:input={() => dispatch("select", { value: choice, index: i })}
|
||||
type="radio"
|
||||
name="radio-{elem_id}"
|
||||
value={choice}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher, tick } from "svelte";
|
||||
import { BlockTitle } from "@gradio/atoms";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
export let value: string = "";
|
||||
export let lines: number = 1;
|
||||
@ -21,16 +22,29 @@
|
||||
change: string;
|
||||
submit: undefined;
|
||||
blur: undefined;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
function handle_change(val: string) {
|
||||
dispatch("change", val);
|
||||
}
|
||||
|
||||
function handle_blur(e: FocusEvent) {
|
||||
function handle_blur() {
|
||||
dispatch("blur");
|
||||
}
|
||||
|
||||
function handle_select(event: Event) {
|
||||
const target: HTMLTextAreaElement | HTMLInputElement = event.target as
|
||||
| HTMLTextAreaElement
|
||||
| HTMLInputElement;
|
||||
const text = target.value;
|
||||
const index: [number, number] = [
|
||||
target.selectionStart as number,
|
||||
target.selectionEnd as number
|
||||
];
|
||||
dispatch("select", { value: text.substring(...index), index: index });
|
||||
}
|
||||
|
||||
async function handle_keypress(e: KeyboardEvent) {
|
||||
await tick();
|
||||
if (e.key === "Enter" && e.shiftKey && lines > 1) {
|
||||
@ -106,6 +120,7 @@
|
||||
{disabled}
|
||||
on:keypress={handle_keypress}
|
||||
on:blur={handle_blur}
|
||||
on:select={handle_select}
|
||||
/>
|
||||
{:else if type === "password"}
|
||||
<input
|
||||
@ -118,6 +133,7 @@
|
||||
{disabled}
|
||||
on:keypress={handle_keypress}
|
||||
on:blur={handle_blur}
|
||||
on:select={handle_select}
|
||||
autocomplete=""
|
||||
/>
|
||||
{:else if type === "email"}
|
||||
@ -131,6 +147,7 @@
|
||||
{disabled}
|
||||
on:keypress={handle_keypress}
|
||||
on:blur={handle_blur}
|
||||
on:select={handle_select}
|
||||
autocomplete="email"
|
||||
/>
|
||||
{/if}
|
||||
@ -146,6 +163,7 @@
|
||||
{disabled}
|
||||
on:keypress={handle_keypress}
|
||||
on:blur={handle_blur}
|
||||
on:select={handle_select}
|
||||
/>
|
||||
{/if}
|
||||
</label>
|
||||
|
@ -1,6 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { BlockLabel, Empty } from "@gradio/atoms";
|
||||
import { ModifyUpload } from "@gradio/upload";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
import { tick } from "svelte";
|
||||
|
||||
import type { Styles } from "@gradio/utils";
|
||||
@ -16,6 +18,10 @@
|
||||
export let value: Array<string> | Array<FileData> | null = null;
|
||||
export let style: Styles = { grid: [2], height: "auto" };
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
// tracks whether the value of the gallery was reset
|
||||
let was_reset: boolean = true;
|
||||
|
||||
@ -32,6 +38,8 @@
|
||||
|
||||
let prevValue: string[] | FileData[] | null = value;
|
||||
let selected_image: number | null = null;
|
||||
let old_selected_image: number | null = null;
|
||||
|
||||
$: if (prevValue !== value) {
|
||||
// When value is falsy (clear button or first load),
|
||||
// style.preview determines the selected image
|
||||
@ -74,6 +82,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: {
|
||||
if (selected_image !== old_selected_image) {
|
||||
old_selected_image = selected_image;
|
||||
if (selected_image !== null) {
|
||||
dispatch("select", {
|
||||
index: selected_image,
|
||||
value: _value?.[selected_image][1]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: scroll_to_img(selected_image);
|
||||
|
||||
let el: Array<HTMLButtonElement> = [];
|
||||
|
@ -2,10 +2,13 @@
|
||||
const browser = typeof document !== "undefined";
|
||||
import { colors } from "@gradio/theme";
|
||||
import { get_next_color } from "@gradio/utils";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export let value: Array<[string, string | number]> = [];
|
||||
export let show_legend: boolean = false;
|
||||
export let color_map: Record<string, string> = {};
|
||||
export let selectable: boolean = false;
|
||||
|
||||
let ctx: CanvasRenderingContext2D;
|
||||
|
||||
@ -24,6 +27,10 @@
|
||||
return `rgba(${r}, ${g}, ${b}, ${255 / a})`;
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
let mode: "categories" | "scores";
|
||||
|
||||
$: {
|
||||
@ -102,7 +109,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div class="textfield">
|
||||
{#each value as [text, category]}
|
||||
{#each value as [text, category], i}
|
||||
<span
|
||||
class="textspan"
|
||||
style:background-color={category === null ||
|
||||
@ -111,6 +118,10 @@
|
||||
: _color_map[category].secondary}
|
||||
class:no-cat={category === null || (active && active !== category)}
|
||||
class:hl={category !== null}
|
||||
class:selectable
|
||||
on:click={() => {
|
||||
dispatch("select", { index: i, value: [text, category] });
|
||||
}}
|
||||
>
|
||||
<span class:no-label={!_color_map[category]} class="text">{text}</span>
|
||||
{#if !show_legend && category !== null}
|
||||
@ -194,10 +205,10 @@
|
||||
|
||||
.textfield {
|
||||
box-sizing: border-box;
|
||||
margin-top: var(--size-7);
|
||||
border-radius: var(--radius-xs);
|
||||
background: var(--color-background-primary);
|
||||
background-color: transparent;
|
||||
padding: var(--block-padding);
|
||||
max-width: var(--size-full);
|
||||
line-height: var(--scale-4);
|
||||
word-break: break-all;
|
||||
@ -246,4 +257,8 @@
|
||||
.no-label {
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
||||
.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,11 +1,17 @@
|
||||
<script lang="ts">
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
import { createEventDispatcher } from "svelte";
|
||||
|
||||
export let value: {
|
||||
label: string;
|
||||
confidences?: Array<{ label: string; confidence: number }>;
|
||||
};
|
||||
|
||||
const dispatch = createEventDispatcher<{ select: SelectData }>();
|
||||
|
||||
export let show_label: boolean = true;
|
||||
export let color: string | undefined = undefined;
|
||||
export let selectable: boolean = false;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@ -18,8 +24,14 @@
|
||||
{value.label}
|
||||
</div>
|
||||
{#if typeof value === "object" && value.confidences}
|
||||
{#each value.confidences as confidence_set}
|
||||
<div class="confidence-set group">
|
||||
{#each value.confidences as confidence_set, i}
|
||||
<div
|
||||
class="confidence-set group"
|
||||
class:selectable
|
||||
on:click={() => {
|
||||
dispatch("select", { index: i, value: confidence_set.label });
|
||||
}}
|
||||
>
|
||||
<div class="inner-wrap ">
|
||||
<div class="bar" style="width: {confidence_set.confidence * 100}%" />
|
||||
<div class="label">
|
||||
@ -101,4 +113,7 @@
|
||||
margin-left: auto;
|
||||
text-align: right;
|
||||
}
|
||||
.selectable {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -6,6 +6,7 @@
|
||||
import { Upload } from "@gradio/upload";
|
||||
import { Button } from "@gradio/button";
|
||||
import EditableCell from "./EditableCell.svelte";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
type Datatype = "str" | "markdown" | "html" | "number" | "bool" | "date";
|
||||
|
||||
@ -37,10 +38,19 @@
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: { data: Array<Array<string | number>>; headers: Array<string> };
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
let editing: boolean | string = false;
|
||||
let selected: boolean | string = false;
|
||||
let editing: false | string = false;
|
||||
let selected: false | string = false;
|
||||
$: {
|
||||
if (selected !== false) {
|
||||
const loc = selected.split("-");
|
||||
const row = parseInt(loc[0]);
|
||||
const col = parseInt(loc[1]);
|
||||
dispatch("select", { index: [row, col], value: data[row][col].value });
|
||||
}
|
||||
}
|
||||
let els: Record<
|
||||
string,
|
||||
{ cell: null | HTMLTableCellElement; input: null | HTMLInputElement }
|
||||
@ -329,7 +339,7 @@
|
||||
sort(col, sort_direction);
|
||||
}
|
||||
|
||||
let header_edit: string | boolean;
|
||||
let header_edit: string | false;
|
||||
|
||||
function update_headers_data() {
|
||||
if (typeof selected === "string") {
|
||||
|
@ -6,5 +6,8 @@
|
||||
"main": "src/index.ts",
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"private": true
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@gradio/utils": "workspace:^0.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -2,22 +2,25 @@
|
||||
import { getContext, onMount, createEventDispatcher, tick } from "svelte";
|
||||
import { TABS } from "./Tabs.svelte";
|
||||
import { Component as Column } from "./../../app/src/components/Column";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
export let elem_id: string = "";
|
||||
export let name: string;
|
||||
export let id: string | number | object = {};
|
||||
|
||||
const dispatch = createEventDispatcher<{ select: undefined }>();
|
||||
const dispatch = createEventDispatcher<{ select: SelectData }>();
|
||||
|
||||
const { register_tab, unregister_tab, selected_tab } = getContext(TABS);
|
||||
const { register_tab, unregister_tab, selected_tab, selected_tab_index } =
|
||||
getContext(TABS);
|
||||
|
||||
register_tab({ name, id });
|
||||
let tab_index = register_tab({ name, id });
|
||||
|
||||
onMount(() => {
|
||||
return () => unregister_tab({ name, id });
|
||||
});
|
||||
|
||||
$: $selected_tab === id && tick().then(() => dispatch("select"));
|
||||
$: $selected_tab_index === tab_index &&
|
||||
tick().then(() => dispatch("select", { value: name, index: tab_index }));
|
||||
</script>
|
||||
|
||||
<div
|
||||
|
@ -5,6 +5,7 @@
|
||||
<script lang="ts">
|
||||
import { setContext, createEventDispatcher, tick } from "svelte";
|
||||
import { writable } from "svelte/store";
|
||||
import type { SelectData } from "@gradio/utils";
|
||||
|
||||
interface Tab {
|
||||
name: string;
|
||||
@ -18,13 +19,18 @@
|
||||
let tabs: Array<Tab> = [];
|
||||
|
||||
const selected_tab = writable<false | object | number | string>(false);
|
||||
const dispatch = createEventDispatcher<{ change: undefined }>();
|
||||
const selected_tab_index = writable<number>(0);
|
||||
const dispatch = createEventDispatcher<{
|
||||
change: undefined;
|
||||
select: SelectData;
|
||||
}>();
|
||||
|
||||
setContext(TABS, {
|
||||
register_tab: (tab: Tab) => {
|
||||
tabs.push({ name: tab.name, id: tab.id });
|
||||
selected_tab.update((current) => current ?? tab.id);
|
||||
tabs = tabs;
|
||||
return tabs.length - 1;
|
||||
},
|
||||
unregister_tab: (tab: Tab) => {
|
||||
const i = tabs.findIndex((t) => t.id === tab.id);
|
||||
@ -33,13 +39,14 @@
|
||||
current === tab.id ? tabs[i]?.id || tabs[tabs.length - 1]?.id : current
|
||||
);
|
||||
},
|
||||
|
||||
selected_tab
|
||||
selected_tab,
|
||||
selected_tab_index
|
||||
});
|
||||
|
||||
function change_tab(id: object | string | number) {
|
||||
selected = id;
|
||||
$selected_tab = id;
|
||||
$selected_tab_index = tabs.findIndex((t) => t.id === id);
|
||||
dispatch("change");
|
||||
}
|
||||
|
||||
@ -54,7 +61,12 @@
|
||||
{t.name}
|
||||
</button>
|
||||
{:else}
|
||||
<button on:click={() => change_tab(t.id)}>
|
||||
<button
|
||||
on:click={() => {
|
||||
change_tab(t.id);
|
||||
dispatch("select", { value: t.name, index: i });
|
||||
}}
|
||||
>
|
||||
{t.name}
|
||||
</button>
|
||||
{/if}
|
||||
|
@ -7,3 +7,9 @@ export const debounce = (callback: Function, wait = 250) => {
|
||||
timeout = setTimeout(next, wait);
|
||||
};
|
||||
};
|
||||
|
||||
export interface SelectData {
|
||||
index: number | [number, number];
|
||||
value: any;
|
||||
selected?: boolean;
|
||||
}
|
||||
|
47
ui/pnpm-lock.yaml
generated
47
ui/pnpm-lock.yaml
generated
@ -254,9 +254,9 @@ importers:
|
||||
cm6-theme-basic-light: ^0.2.0
|
||||
codemirror: ^6.0.1
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.3.0_qnrzx6tcnzw54wkho53dkatgie
|
||||
'@codemirror/autocomplete': 6.3.0
|
||||
'@codemirror/commands': 6.1.2
|
||||
'@codemirror/lang-css': 6.1.0_5zlqs4evqgxsepb3z33ozuzo4y
|
||||
'@codemirror/lang-css': 6.1.0
|
||||
'@codemirror/lang-html': 6.4.2
|
||||
'@codemirror/lang-javascript': 6.1.4
|
||||
'@codemirror/lang-json': 6.0.1
|
||||
@ -274,19 +274,21 @@ importers:
|
||||
'@lezer/common': 1.0.2
|
||||
'@lezer/highlight': 1.1.3
|
||||
'@lezer/markdown': 1.0.2
|
||||
cm6-theme-basic-dark: 0.2.0_xw675pmc2xeonceu42bz2qxoy4
|
||||
cm6-theme-basic-light: 0.2.0_xw675pmc2xeonceu42bz2qxoy4
|
||||
codemirror: 6.0.1_@lezer+common@1.0.2
|
||||
cm6-theme-basic-dark: 0.2.0_bdbdfebd82d5c8e68894e6839d42eec7
|
||||
cm6-theme-basic-light: 0.2.0_bdbdfebd82d5c8e68894e6839d42eec7
|
||||
codemirror: 6.0.1
|
||||
|
||||
packages/file:
|
||||
specifiers:
|
||||
'@gradio/atoms': workspace:^0.0.1
|
||||
'@gradio/icons': workspace:^0.0.1
|
||||
'@gradio/upload': workspace:^0.0.1
|
||||
'@gradio/utils': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/atoms': link:../atoms
|
||||
'@gradio/icons': link:../icons
|
||||
'@gradio/upload': link:../upload
|
||||
'@gradio/utils': link:../utils
|
||||
|
||||
packages/form:
|
||||
specifiers:
|
||||
@ -414,7 +416,10 @@ importers:
|
||||
dequal: 2.0.2
|
||||
|
||||
packages/tabs:
|
||||
specifiers: {}
|
||||
specifiers:
|
||||
'@gradio/utils': workspace:^0.0.1
|
||||
dependencies:
|
||||
'@gradio/utils': link:../utils
|
||||
|
||||
packages/theme:
|
||||
specifiers: {}
|
||||
@ -581,13 +586,8 @@ packages:
|
||||
regenerator-runtime: 0.13.9
|
||||
dev: false
|
||||
|
||||
/@codemirror/autocomplete/6.3.0_qnrzx6tcnzw54wkho53dkatgie:
|
||||
/@codemirror/autocomplete/6.3.0:
|
||||
resolution: {integrity: sha512-4jEvh3AjJZTDKazd10J6ZsCIqaYxDMCeua5ouQxY8hlFIml+nr7le0SgBhT3SIytFBmdzPK3AUhXGuW3T79nVg==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
'@codemirror/state': ^6.0.0
|
||||
'@codemirror/view': ^6.0.0
|
||||
'@lezer/common': ^1.0.0
|
||||
dependencies:
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.1.2
|
||||
@ -604,23 +604,20 @@ packages:
|
||||
'@lezer/common': 1.0.2
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-css/6.1.0_5zlqs4evqgxsepb3z33ozuzo4y:
|
||||
/@codemirror/lang-css/6.1.0:
|
||||
resolution: {integrity: sha512-GYn4TyMvQLrkrhdisFh8HCTDAjPY/9pzwN12hG9UdrTUxRUMicF+8GS24sFEYaleaG1KZClIFLCj0Rol/WO24w==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.3.0_qnrzx6tcnzw54wkho53dkatgie
|
||||
'@codemirror/autocomplete': 6.3.0
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.1.2
|
||||
'@lezer/css': 1.1.1
|
||||
transitivePeerDependencies:
|
||||
- '@codemirror/view'
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/@codemirror/lang-html/6.4.2:
|
||||
resolution: {integrity: sha512-bqCBASkteKySwtIbiV/WCtGnn/khLRbbiV5TE+d9S9eQJD7BA4c5dTRm2b3bVmSpilff5EYxvB4PQaZzM/7cNw==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.3.0_qnrzx6tcnzw54wkho53dkatgie
|
||||
'@codemirror/lang-css': 6.1.0_5zlqs4evqgxsepb3z33ozuzo4y
|
||||
'@codemirror/autocomplete': 6.3.0
|
||||
'@codemirror/lang-css': 6.1.0
|
||||
'@codemirror/lang-javascript': 6.1.4
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/state': 6.1.2
|
||||
@ -633,7 +630,7 @@ packages:
|
||||
/@codemirror/lang-javascript/6.1.4:
|
||||
resolution: {integrity: sha512-OxLf7OfOZBTMRMi6BO/F72MNGmgOd9B0vetOLvHsDACFXayBzW8fm8aWnDM0yuy68wTK03MBf4HbjSBNRG5q7A==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.3.0_qnrzx6tcnzw54wkho53dkatgie
|
||||
'@codemirror/autocomplete': 6.3.0
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/lint': 6.0.0
|
||||
'@codemirror/state': 6.1.2
|
||||
@ -1554,7 +1551,7 @@ packages:
|
||||
engines: {node: '>=0.8'}
|
||||
dev: false
|
||||
|
||||
/cm6-theme-basic-dark/0.2.0_xw675pmc2xeonceu42bz2qxoy4:
|
||||
/cm6-theme-basic-dark/0.2.0_bdbdfebd82d5c8e68894e6839d42eec7:
|
||||
resolution: {integrity: sha512-+mNNJecRtxS/KkloMDCQF0oTrT6aFGRZTjnBcdT5UG1pcDO4Brq8l1+0KR/8dZ7hub2gOGOzoi3rGFD8GzlH7Q==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
@ -1568,7 +1565,7 @@ packages:
|
||||
'@lezer/highlight': 1.1.3
|
||||
dev: false
|
||||
|
||||
/cm6-theme-basic-light/0.2.0_xw675pmc2xeonceu42bz2qxoy4:
|
||||
/cm6-theme-basic-light/0.2.0_bdbdfebd82d5c8e68894e6839d42eec7:
|
||||
resolution: {integrity: sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
@ -1582,18 +1579,16 @@ packages:
|
||||
'@lezer/highlight': 1.1.3
|
||||
dev: false
|
||||
|
||||
/codemirror/6.0.1_@lezer+common@1.0.2:
|
||||
/codemirror/6.0.1:
|
||||
resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
|
||||
dependencies:
|
||||
'@codemirror/autocomplete': 6.3.0_qnrzx6tcnzw54wkho53dkatgie
|
||||
'@codemirror/autocomplete': 6.3.0
|
||||
'@codemirror/commands': 6.1.2
|
||||
'@codemirror/language': 6.6.0
|
||||
'@codemirror/lint': 6.0.0
|
||||
'@codemirror/search': 6.2.2
|
||||
'@codemirror/state': 6.1.2
|
||||
'@codemirror/view': 6.4.1
|
||||
transitivePeerDependencies:
|
||||
- '@lezer/common'
|
||||
dev: false
|
||||
|
||||
/color-convert/1.9.3:
|
||||
|
Loading…
x
Reference in New Issue
Block a user