/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
frontend/lib/src/components/widgets/DataFrame/columns/LinkColumn.ts
219 строк
7 KB
Ken McGrady
Update copyright header to 2026 (#13491)
02 янв 2026, 16:21
Не верифицирован
02 янв 2026, 16:21
374540a
Код
Авторство
О чём код?
/** * 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 { GridCell, GridCellKind, UriCell } from "@glideapps/glide-data-grid" import { isMaterialIcon, parseIconPackEntry, } from "~lib/components/shared/Icon/DynamicIcon" import { genericFonts } from "~lib/theme/primitives/typography" import { isNullOrUndefined } from "~lib/util/utils" import { BaseColumn, BaseColumnProps, getErrorCell, getLinkDisplayValueFromRegex, toSafeString, } from "./utils" export interface LinkColumnParams { /** * The maximum number of characters the user can enter into the text input. */ readonly max_chars?: number /** * Regular expression that the input's value must match for the value to pass. */ readonly validate?: string /** * A value to display in the link cell. This can be a regex to parse out a * specific substring of the url to be displayed. This can also be a * material icon specified via ":material/icon_name:". */ readonly display_text?: string } /** * The link column is a special column that interprets the cell content as * an hyperlink / url and allows the user to click on it. */ function LinkColumn(props: BaseColumnProps): BaseColumn { const parameters = (props.columnTypeOptions as LinkColumnParams) || {} let validateRegex: RegExp | string | undefined = undefined if (parameters.validate) { // Prepare the validation regex: try { // u flag allows unicode characters // s flag allows . to match newlines validateRegex = new RegExp(parameters.validate, "us") } catch (error) { // Put error message in validateRegex so we can display it in the cell // eslint-disable-next-line @typescript-eslint/restrict-template-expressions validateRegex = `Invalid validate regex: ${parameters.validate}.\nError: ${error}` } } let usesDisplayIcon = false let configuredDisplayText = parameters.display_text // Determine if the user's provided display text is a regexp pattern or not. let displayTextRegex: RegExp | undefined = undefined if (!isNullOrUndefined(configuredDisplayText)) { if ( configuredDisplayText.startsWith(":material/") && isMaterialIcon(configuredDisplayText) ) { // We need to only use the icon name in the display text so // that the icon font can correctly resolve the icon. configuredDisplayText = parseIconPackEntry(configuredDisplayText).icon usesDisplayIcon = true } else if ( configuredDisplayText.includes("(") && configuredDisplayText.includes(")") ) { try { displayTextRegex = new RegExp(configuredDisplayText, "us") // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (error) { // The regex is invalid, interpret it as static display text. displayTextRegex = undefined } } } const cellTemplate: UriCell = { kind: GridCellKind.Uri, readonly: !props.isEditable, allowOverlay: !usesDisplayIcon, contentAlign: props.contentAlignment ?? (usesDisplayIcon ? "center" : undefined), style: "normal", hoverEffect: true, data: "", displayData: "", copyData: "", ...(usesDisplayIcon && { themeOverride: { // Configure icon font and reset link color to default text color: fontFamily: genericFonts.iconFont, linkColor: undefined, }, }), } const validateInput = (href?: string): boolean => { if (isNullOrUndefined(href)) { if (props.isRequired) { return false } return true } const cellHref = toSafeString(href) if (parameters.max_chars && cellHref.length > parameters.max_chars) { // value is too long return false } if ( validateRegex instanceof RegExp && validateRegex.test(cellHref) === false ) { return false } return true } return { ...props, kind: "link", typeIcon: ":material/link:", sortMode: "default", validateInput, // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type. getCell(data?: any, validate?: boolean): GridCell { if (isNullOrUndefined(data)) { return { ...cellTemplate, // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type. data: null as any, isMissingValue: true, onClickUri: () => {}, themeOverride: undefined, } as UriCell } const href: string = data if (typeof validateRegex === "string") { // The regex is invalid, we return an error to indicate this // to the developer: return getErrorCell(toSafeString(href), validateRegex) } if (validate) { const validationResult = validateInput(href) if (validationResult === false) { // The input is invalid, we return an error cell which will // prevent this cell to be inserted into the table. // This cell should never be actually displayed to the user. // It's mostly used internally to prevent invalid input to be // inserted into the table. return getErrorCell(toSafeString(href), "Invalid input.") } } let displayText = "" if (href) { if (displayTextRegex !== undefined) { // Set display value to be the regex extracted portion of the href. displayText = getLinkDisplayValueFromRegex(displayTextRegex, href) } else { // Use user provided display_text unless it's null, undefined, or an empty string. // If it's any of those falsy values, use the href. displayText = configuredDisplayText || href } } return { ...cellTemplate, data: href, displayData: displayText, isMissingValue: isNullOrUndefined(href), onClickUri: a => { window.open( href.startsWith("www.") ? `https://${href}` : href, "_blank", "noopener,noreferrer" ) a.preventDefault() }, copyData: href, } as UriCell }, getCellValue(cell: UriCell): string | null { return isNullOrUndefined(cell.data) ? null : cell.data }, } } LinkColumn.isEditableType = true export default LinkColumn