Spaces:
Running
Running
from typing import Callable | |
import gradio as gr | |
import modelscope_studio.components.antd as antd | |
import modelscope_studio.components.base as ms | |
from gradio.components.base import Component | |
class Site: | |
def __init__(self, | |
tabs: list, | |
docs: dict, | |
default_active_tab: str | None = None, | |
logo: Component | Callable | None = None): | |
self.tabs = tabs | |
self.docs = docs | |
self.default_active_tab = default_active_tab | |
self.default_active_tab_item = next( | |
(item for item in self.tabs if item["key"] == default_active_tab), | |
{}) | |
self.logo = logo | |
def _render_docs(self, items: list, tab: dict): | |
docs_tabs = [] | |
for item in items: | |
if "children" in item: | |
docs_tabs.extend(self._render_docs(item["children"], tab)) | |
elif "key" in item: | |
key = item["key"].replace("-", "_") | |
tab_docs = self.docs.get(tab["key"], {}) | |
if key in tab_docs: | |
with antd.Tabs.Item( | |
key=key, | |
visible=True if tab.get("default_active_key") | |
== key else False) as docs_tab: | |
docs_tabs.append(docs_tab) | |
tab_docs[key].render() | |
return docs_tabs | |
def render(self): | |
def on_tab_menu_select(e: gr.EventData): | |
selected_tab = e._data["payload"][0]["key"] | |
item = next( | |
(item for item in tab_components if item.key == selected_tab), | |
tab_components[0]) | |
return { | |
tab_menu: gr.update(selected_keys=[selected_tab]), | |
tabs: gr.update(active_key=selected_tab), | |
item: gr.update(visible=True) | |
} | |
# Css is not working in demo if there are multiple Blocks, so we need to add css from demos | |
css = "" | |
for category in self.docs: | |
for component in self.docs[category]: | |
css += self.docs[category][component].get_css() | |
with gr.Blocks(css=css + """ | |
.gradio-container { | |
max-width: 100% !important; | |
padding: 0 !important; | |
} | |
.gradio-container > main.fillable { | |
max-width: 100% !important; | |
padding: 0 !important; | |
} | |
.docs-layout-sider { | |
width: 100% !important; | |
max-width: 100% !important; | |
} | |
""") as demo: | |
with ms.Application() as app: | |
with antd.ConfigProvider(): | |
with antd.Layout(elem_style=dict( | |
height= | |
"calc(100vh - var(--size-4) - var(--body-text-size) * 1.5)" | |
)): | |
with antd.Layout.Header(elem_style=dict( | |
padding='0 16px', | |
backgroundColor= | |
"var(--ms-gr-ant-color-bg-container)")): | |
with antd.Flex(align='center', gap=8): | |
if self.logo: | |
with antd.Flex( | |
justify="center", | |
align='center', | |
elem_style=dict(height='100%')): | |
if callable(self.logo): | |
self.logo() | |
else: | |
self.logo.render() | |
tab_menu = antd.Menu( | |
mode="horizontal", | |
selected_keys=[self.default_active_tab], | |
items=self.tabs, | |
elem_style=dict(flex=1, minWidth=0)) | |
with ms.AutoLoading(show_mask=True): | |
with antd.Tabs( | |
active_key=self.default_active_tab, | |
render_tab_bar="() => null", | |
) as tabs: | |
tab_components = [] | |
for tab in self.tabs: | |
with antd.Tabs.Item( | |
key=tab["key"], | |
elem_style=dict( | |
height= | |
"calc(100vh - var(--size-4) - var(--body-text-size) * 1.5 - 64px)" | |
), | |
visible=True | |
if self.default_active_tab | |
== tab["key"] else | |
False) as tab_item: | |
tab_components.append(tab_item) | |
if "content" in tab: | |
# custom render | |
with ms.Div(elem_style=dict( | |
maxHeight="100%", | |
overflow="auto")): | |
tab["content"].render() | |
elif "menus" in tab: | |
# menus render | |
with antd.Layout(elem_style=dict( | |
height='100%')): | |
with antd.Splitter(): | |
with antd.Splitter.Panel( | |
default_size=213, | |
max="50%", | |
min=82, | |
elem_style=dict( | |
backgroundColor= | |
"var(--ms-gr-ant-color-bg-container)" | |
)) as sider_panel: | |
with antd.Layout.Sider( | |
breakpoint="sm", | |
trigger=None, | |
elem_classes= | |
"docs-layout-sider", | |
elem_style=dict( | |
# height= | |
# "calc(100vh - 64px)", | |
# overflow= | |
# "auto", | |
position= | |
"relative", | |
backgroundColor | |
="var(--ms-gr-ant-color-bg-container)" | |
)): | |
sider_menu = antd.Menu( | |
selected_keys=[ | |
tab.get( | |
"default_active_key", | |
None) | |
], | |
mode="inline", | |
items=tab.get( | |
"menus", | |
[])) | |
if "extra_menu_footer" in tab: | |
if callable(tab[ | |
"extra_menu_footer"] | |
): | |
tab["extra_menu_footer"]( | |
) | |
else: | |
tab["extra_menu_footer"].render( | |
) | |
with antd.Splitter.Panel(): | |
with antd.Layout( | |
elem_style=dict( | |
width= | |
"100%", | |
backgroundColor | |
="var(--ms-gr-ant-color-bg-container)" | |
)): | |
with antd.Layout.Content( | |
elem_style= | |
dict( | |
padding= | |
'12px 28px', | |
overflow | |
="auto" | |
)): | |
with antd.Tabs( | |
active_key | |
=tab. | |
get( | |
"default_active_key", | |
None | |
), | |
render_tab_bar | |
="() => null" | |
) as layout_content_tabs: | |
docs_tabs = self._render_docs( | |
tab. | |
get( | |
"menus", | |
[] | |
), tab) | |
def on_layout_menu_select_wrapper( | |
tabs, sider_menu, | |
layout_content_tabs): | |
def on_layout_menu_select( | |
e: gr.EventData): | |
selected_menu = e._data[ | |
"payload"][0]["key"] | |
item = next( | |
(item for item in tabs | |
if item.key == | |
selected_menu), tabs[0]) | |
return { | |
sider_menu: | |
gr.update(selected_keys=[ | |
selected_menu | |
]), | |
layout_content_tabs: | |
gr.update( | |
active_key=selected_menu | |
), | |
item: | |
gr.update(visible=True) | |
} | |
return on_layout_menu_select | |
sider_menu.select( | |
fn=on_layout_menu_select_wrapper( | |
docs_tabs, sider_menu, | |
layout_content_tabs), | |
outputs=[ | |
sider_menu, layout_content_tabs, | |
*docs_tabs | |
]) | |
def on_app_mount(e: gr.EventData): | |
screen_width = e._data["screen"][ | |
"width"] | |
return gr.update( | |
default_size=82 if screen_width < | |
576 else 213) | |
app.mount(on_app_mount, | |
outputs=[sider_panel]) | |
tab_menu.select( | |
fn=on_tab_menu_select, | |
outputs=[tab_menu, tabs, *tab_components]) | |
return demo | |