/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
frontend/app/src/components/Navigation/TopNavSection.tsx
193 строки
6 KB
Maya Barnes
Add shadows to theming (#13525)
16 янв 2026, 01:23
Не верифицирован
16 янв 2026, 01:23
ce817f8
Код
Авторство
О чём код?
/** * Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { Fragment, useState } from "react" import { KeyboardArrowDown, KeyboardArrowUp, } from "@emotion-icons/material-outlined" import { PLACEMENT, TRIGGER_TYPE, Popover as UIPopover } from "baseui/popover" import { StreamlitEndpoints } from "@streamlit/connection" import { Icon, useEmotionTheme } from "@streamlit/lib" import { IAppPage } from "@streamlit/protobuf" import { isNullOrUndefined } from "@streamlit/utils" import { StyledIconContainer, StyledNavSection, StyledNavSectionText, StyledPopoverContent, StyledSectionName, StyledTopNavSidebarNavLinkContainer, } from "./styled-components" import { SidebarNavLink } from "./index" interface TopNavSectionProps { handlePageChange: (pageScriptHash: string) => void title: string sections: IAppPage[][] endpoints: StreamlitEndpoints pageLinkBaseUrl: string currentPageScriptHash: string hideChevron?: boolean widgetsDisabled: boolean } const TopNavSection = ({ title, sections, handlePageChange, endpoints, pageLinkBaseUrl, currentPageScriptHash, hideChevron = false, widgetsDisabled, }: TopNavSectionProps): React.ReactElement | null => { const [open, setOpen] = useState(false) const theme = useEmotionTheme() const showSections = sections.length > 1 if ( isNullOrUndefined(sections) || sections.length === 0 || sections[0].length === 0 ) { return null } return ( <UIPopover triggerType={TRIGGER_TYPE.click} placement={PLACEMENT.bottomLeft} content={() => ( <StyledPopoverContent data-testid="stTopNavPopover"> {sections.map((section, _sectionIndex) => { const sectionName = section[0].sectionHeader return section.map((item, index) => { const handleClick = (e: React.MouseEvent): boolean => { e.preventDefault() if (item.pageScriptHash) { handlePageChange(item.pageScriptHash) } setOpen(false) return false } // Convert potentially null pageName to string safely const pageName = String(item.pageName || "") return ( <Fragment key={`${item.pageScriptHash}-${pageName}`}> {index === 0 && showSections && ( <StyledSectionName>{sectionName}</StyledSectionName> )} <StyledTopNavSidebarNavLinkContainer> <SidebarNavLink {...item} icon={item.icon || null} isTopNav={true} isInDropdown={true} isActive={currentPageScriptHash === item.pageScriptHash} onClick={handleClick} pageUrl={endpoints.buildAppPageURL( pageLinkBaseUrl, item )} widgetsDisabled={widgetsDisabled} > {pageName} </SidebarNavLink> </StyledTopNavSidebarNavLinkContainer> </Fragment> ) }) })} </StyledPopoverContent> )} isOpen={open} onClickOutside={() => setOpen(false)} onClick={() => (open ? setOpen(false) : undefined)} onEsc={() => setOpen(false)} // Consistently render the content for smoother opening/closing renderAll={true} overrides={{ Body: { style: () => ({ marginTop: theme.spacing.sm, marginRight: theme.spacing.lg, marginBottom: theme.spacing.lg, maxHeight: "70vh", minWidth: "8rem", overflow: "auto", maxWidth: `calc(${theme.sizes.contentMaxWidth} - 2*${theme.spacing.lg})`, borderTopLeftRadius: theme.radii.xl, borderTopRightRadius: theme.radii.xl, borderBottomRightRadius: theme.radii.xl, borderBottomLeftRadius: theme.radii.xl, borderLeftWidth: theme.sizes.borderWidth, borderRightWidth: theme.sizes.borderWidth, borderTopWidth: theme.sizes.borderWidth, borderBottomWidth: theme.sizes.borderWidth, borderLeftStyle: "solid", borderRightStyle: "solid", borderTopStyle: "solid", borderBottomStyle: "solid", borderLeftColor: theme.colors.borderColor, borderRightColor: theme.colors.borderColor, borderTopColor: theme.colors.borderColor, borderBottomColor: theme.colors.borderColor, boxShadow: theme.shadows.popover, [`@media (max-width: ${theme.breakpoints.sm})`]: { maxWidth: `calc(100% - ${theme.spacing.threeXL})`, }, }), }, }} > <div> <StyledNavSection tabIndex={0} onClick={() => setOpen(!open)} isOpen={open} data-testid="stTopNavSection" > <StyledNavSectionText>{title}</StyledNavSectionText> {!hideChevron && ( <StyledIconContainer> <Icon content={open ? KeyboardArrowUp : KeyboardArrowDown} size="lg" /> </StyledIconContainer> )} </StyledNavSection> </div> </UIPopover> ) } export default TopNavSection