mirror of
https://github.com/gradio-app/gradio.git
synced 2025-01-30 11:00:11 +08:00
Refactor gr.ParamViewer
to use HTML <details>
and other tweaks (#8744)
* changes * changes * revert demo change * add changeset * changes * format * fixes * param viewer * update demo * add changeset * docstrings --------- Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
parent
fb0daf3730
commit
b736c8db34
6
.changeset/heavy-beds-exist.md
Normal file
6
.changeset/heavy-beds-exist.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"@gradio/paramviewer": patch
|
||||
"gradio": patch
|
||||
---
|
||||
|
||||
feat:Refactor `gr.ParamViewer` to use HTML `<details>` and other tweaks
|
@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Literal, TypedDict
|
||||
from typing import TYPE_CHECKING, Literal, Mapping, TypedDict
|
||||
|
||||
from gradio_client.documentation import document
|
||||
|
||||
@ -22,7 +22,7 @@ class ParamViewer(Component):
|
||||
"""
|
||||
Displays an interactive table of parameters and their descriptions and default values with syntax highlighting. For each parameter,
|
||||
the user should provide a type (e.g. a `str`), a human-readable description, and a default value. As this component does not accept user input,
|
||||
it is rarely used as an input component.Internally, this component is used to display the parameters of components in the Custom
|
||||
it is rarely used as an input component. Internally, this component is used to display the parameters of components in the Custom
|
||||
Component Gallery (https://www.gradio.app/custom-components/gallery).
|
||||
"""
|
||||
|
||||
@ -33,27 +33,30 @@ class ParamViewer(Component):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: dict[str, Parameter] | None = None,
|
||||
value: Mapping[str, Parameter] | None = None,
|
||||
language: Literal["python", "typescript"] = "python",
|
||||
linkify: list[str] | None = None,
|
||||
every: Timer | float | None = None,
|
||||
inputs: Component | list[Component] | set[Component] | None = None,
|
||||
render: bool = True,
|
||||
key: int | str | None = None,
|
||||
header: str | None = "Parameters",
|
||||
):
|
||||
"""
|
||||
Parameters:
|
||||
value: A list of dictionaries with keys "type", "description", and "default" for each parameter.
|
||||
value: A dictionary of dictionaries. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and "default" for each parameter.
|
||||
language: The language to display the code in. One of "python" or "typescript".
|
||||
linkify: A list of strings to linkify. If any of these strings is found in the description, it will be rendered as a link.
|
||||
every: Continously calls `value` to recalculate it if `value` is a function (has no effect otherwise). Can provide a Timer whose tick resets `value`, or a float that provides the regular interval for the reset Timer.
|
||||
inputs: Components that are used as inputs to calculate `value` if `value` is a function (has no effect otherwise). `value` is recalculated any time the inputs change.
|
||||
render: If False, component will not render be rendered in the Blocks context. Should be used if the intention is to assign event listeners now but render the component later.
|
||||
key: if assigned, will be used to assume identity across a re-render. Components that have the same key across a re-render will have their value preserved.
|
||||
header: The header to display above the table of parameters, also includes a toggle button that closes/opens all details at once. If None, no header will be displayed.
|
||||
"""
|
||||
self.value = value or {}
|
||||
self.language = language
|
||||
self.linkify = linkify
|
||||
self.header = header
|
||||
super().__init__(
|
||||
every=every,
|
||||
inputs=inputs,
|
||||
@ -65,16 +68,16 @@ class ParamViewer(Component):
|
||||
def preprocess(self, payload: dict[str, Parameter]) -> dict[str, Parameter]:
|
||||
"""
|
||||
Parameters:
|
||||
payload: A `dict[str, dict]`. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and (optionally) "default" for each parameter.
|
||||
payload: A `dict[str, dict]`. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and "default" for each parameter.
|
||||
Returns:
|
||||
(Rarely used) passes value as a `dict[str, dict]`. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and (optionally) "default" for each parameter.
|
||||
(Rarely used) passes value as a `dict[str, dict]`. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and "default" for each parameter.
|
||||
"""
|
||||
return payload
|
||||
|
||||
def postprocess(self, value: dict[str, Parameter]) -> dict[str, Parameter]:
|
||||
"""
|
||||
Parameters:
|
||||
value: Expects value as a `dict[str, dict]`. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and (optionally) "default" for each parameter.
|
||||
value: Expects value as a `dict[str, dict]`. The key in the outer dictionary is the parameter name, while the inner dictionary has keys "type", "description", and "default" for each parameter.
|
||||
Returns:
|
||||
The same value.
|
||||
"""
|
||||
|
@ -11,6 +11,7 @@
|
||||
>;
|
||||
|
||||
export let linkify: string[] = [];
|
||||
export let header: string | null = null;
|
||||
</script>
|
||||
|
||||
<ParamViewer docs={value} {linkify} />
|
||||
<ParamViewer docs={value} {linkify} {header} />
|
||||
|
@ -13,17 +13,19 @@
|
||||
}
|
||||
|
||||
export let docs: Record<string, Param>;
|
||||
|
||||
export let lang: "python" | "typescript" = "python";
|
||||
export let linkify: string[] = [];
|
||||
export let header: string | null;
|
||||
|
||||
let component_root: HTMLElement;
|
||||
let _docs: Param[];
|
||||
let all_open = false;
|
||||
|
||||
$: {
|
||||
setTimeout(() => {
|
||||
_docs = highlight_code(docs, lang);
|
||||
}, 0);
|
||||
}
|
||||
$: show_desc = _docs && _docs.map((x) => false);
|
||||
|
||||
function highlight(code: string, lang: "python" | "typescript"): string {
|
||||
let highlighted = Prism.highlight(code, Prism.languages[lang], lang);
|
||||
@ -55,50 +57,95 @@
|
||||
}
|
||||
);
|
||||
}
|
||||
let el = [];
|
||||
|
||||
function toggle_all(): void {
|
||||
all_open = !all_open;
|
||||
const details = component_root.querySelectorAll(".param");
|
||||
details.forEach((detail) => {
|
||||
if (detail instanceof HTMLDetailsElement) {
|
||||
detail.open = all_open;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="wrap">
|
||||
<div class="wrap" bind:this={component_root}>
|
||||
{#if header !== null}
|
||||
<div class="header">
|
||||
<span class="title">{header}</span>
|
||||
<button
|
||||
class="toggle-all"
|
||||
on:click={toggle_all}
|
||||
title={all_open ? "Close All" : "Open All"}
|
||||
>
|
||||
▼
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
{#if _docs}
|
||||
{#each _docs as { type, description, default: _default, name }, i (name)}
|
||||
<div class="param md" class:open={show_desc[i]}>
|
||||
<div class="type">
|
||||
<pre class="language-{lang}"><code bind:this={el[i]}
|
||||
{#each _docs as { type, description, default: _default, name } (name)}
|
||||
<details class="param md">
|
||||
<summary class="type">
|
||||
<pre class="language-{lang}"><code
|
||||
>{name}{#if type}: {@html type}{/if}</code
|
||||
></pre>
|
||||
<button
|
||||
on:click={() => (show_desc[i] = !show_desc[i])}
|
||||
class="arrow"
|
||||
class:disabled={!description && !_default}
|
||||
class:hidden={!show_desc[i]}>▲</button
|
||||
>
|
||||
</div>
|
||||
{#if show_desc[i]}
|
||||
{#if _default}
|
||||
<div class="default" class:last={!description}>
|
||||
<span style:padding-right={"4px"}>default</span>
|
||||
<code>= {@html _default}</code>
|
||||
</div>
|
||||
{/if}
|
||||
{#if description}
|
||||
<div class="description"><p>{description}</p></div>
|
||||
{/if}
|
||||
</summary>
|
||||
{#if _default}
|
||||
<div class="default" class:last={!description}>
|
||||
<span style:padding-right={"4px"}>default</span>
|
||||
<code>= {@html _default}</code>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if description}
|
||||
<div class="description"><p>{description}</p></div>
|
||||
{/if}
|
||||
</details>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.7rem 1rem;
|
||||
border-bottom: 1px solid var(--table-border-color);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: var(--scale-0);
|
||||
font-weight: 600;
|
||||
color: var(--body-text-color);
|
||||
}
|
||||
|
||||
.toggle-all {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
color: var(--body-text-color);
|
||||
font-size: 0.7em;
|
||||
line-height: 1;
|
||||
opacity: 0.7;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
transform 0.3s ease;
|
||||
}
|
||||
|
||||
.toggle-all:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:global(.wrap[data-all-open="true"]) .toggle-all {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.default :global(pre),
|
||||
.default :global(.highlight) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.disbaled {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.wrap :global(pre),
|
||||
.wrap :global(.highlight) {
|
||||
margin: 0 !important;
|
||||
@ -142,7 +189,6 @@
|
||||
border-width: var(--block-border-width);
|
||||
border-color: var(--block-border-color);
|
||||
border-radius: var(--block-radius);
|
||||
background: var(--table-odd-background-fill);
|
||||
width: 100%;
|
||||
line-height: var(--line-sm);
|
||||
color: var(--body-text-color);
|
||||
@ -153,22 +199,22 @@
|
||||
padding: 0.7rem 1rem;
|
||||
background: var(--table-odd-background-fill);
|
||||
border-bottom: 0px solid var(--table-border-color);
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
.type::after {
|
||||
content: "▼";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
transform: rotate(180deg);
|
||||
height: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transform: translateY(-50%);
|
||||
transition: transform 0.3s ease;
|
||||
font-size: 0.7em;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.arrow.hidden {
|
||||
transform: rotate(270deg);
|
||||
details[open] .type::after {
|
||||
transform: translateY(-50%) rotate(180deg);
|
||||
}
|
||||
|
||||
.default {
|
||||
@ -196,15 +242,19 @@
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.param:last-child .description {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.open .type {
|
||||
details[open] .type {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.param.md code {
|
||||
background: none;
|
||||
}
|
||||
|
||||
details > summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details > summary::-webkit-details-marker {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue
Block a user