/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
frontend/lib/src/components/widgets/DataFrame/columns/NumberColumn.ts
245 строк
8 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, NumberCell } from "@glideapps/glide-data-grid" import { format as formatArrowCell } from "~lib/dataframes/arrowFormatUtils" import { isDurationType, isIntegerType, isPeriodType, isUnsignedIntegerType, } from "~lib/dataframes/arrowTypeUtils" import { formatNumber } from "~lib/util/formatNumber" import { isNullOrUndefined, notNullOrUndefined } from "~lib/util/utils" import { BaseColumn, BaseColumnProps, countDecimals, getErrorCell, mergeColumnParameters, toSafeNumber, toSafeString, truncateDecimals, } from "./utils" export interface NumberColumnParams { /** * The minimum allowed value for editing. Is set to 0 for unsigned values. */ readonly min_value?: number /** * The maximum allowed value for editing. */ readonly max_value?: number /** * A formatting syntax (e.g. sprintf) to format the display value. * This can be used for adding prefix or suffix, or changing the number of * decimals of the display value. */ readonly format?: string /** * Specifies the granularity that the value must adhere. * This will also influence the maximum precision. This will impact the * number of decimals allowed to be entered as well as the number of * decimals displayed (if format is not specified). * This is set to 1 for integer types. */ readonly step?: number } /** * A column types that supports optimized rendering and editing for numbers. * This supports float, integer, and unsigned integer types. */ function NumberColumn(props: BaseColumnProps): BaseColumn { const parameters = mergeColumnParameters( // Default parameters: { // Set step to 1 for integer types step: isIntegerType(props.arrowType) ? 1 : undefined, // if uint (unsigned int), only positive numbers are allowed min_value: isUnsignedIntegerType(props.arrowType) ? 0 : undefined, } as NumberColumnParams, // User parameters: props.columnTypeOptions ) as NumberColumnParams // If no custom format is provided & the column type is duration or period, // instruct the column to use the arrow formatting for the display value. const useArrowFormatting = !parameters.format && (isDurationType(props.arrowType) || isPeriodType(props.arrowType)) const allowNegative = isNullOrUndefined(parameters.min_value) || parameters.min_value < 0 const fixedDecimals = notNullOrUndefined(parameters.step) && !Number.isNaN(parameters.step) ? countDecimals(parameters.step) : undefined const cellTemplate: NumberCell = { kind: GridCellKind.Number, data: undefined, displayData: "", readonly: !props.isEditable, allowOverlay: true, contentAlign: props.contentAlignment || useArrowFormatting ? "left" : "right", // The text in pinned columns should be faded. style: props.isPinned ? "faded" : "normal", allowNegative, fixedDecimals, // We don't want to show any thousand separators // in the cell overlay/editor: thousandSeparator: "", } // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: Replace 'any' with a more specific type. const validateInput = (data?: any): boolean | number => { let cellData: number | null = toSafeNumber(data) if (isNullOrUndefined(cellData)) { if (props.isRequired) { return false } return true } if (Number.isNaN(cellData)) { return false } // A flag to indicate whether the value has been auto-corrected. // This is used to decide if we should return the corrected value or true. // But we still run all other validations on the corrected value below. let corrected = false // Apply max_value configuration option: if ( notNullOrUndefined(parameters.max_value) && cellData > parameters.max_value ) { cellData = parameters.max_value corrected = true } // Apply min_value configuration option: if ( notNullOrUndefined(parameters.min_value) && cellData < parameters.min_value ) { // Only return false, since correcting it negatively impacts // the user experience. return false } // TODO(lukasmasuch): validate step size? // if (notNullOrUndefined(parameters.step) && parameters.step !== 1) return corrected ? cellData : true } return { ...props, kind: "number", sortMode: "smart", typeIcon: ":material/tag:", 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 (validate === true) { const validationResult = validateInput(data) 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(data), "Invalid input.") } else if (typeof validationResult === "number") { // Apply corrections: data = validationResult } } let cellData: number | null = toSafeNumber(data) let displayData = "" if (notNullOrUndefined(cellData)) { if (Number.isNaN(cellData)) { return getErrorCell( toSafeString(data), "The value cannot be interpreted as a number." ) } // Cut decimals: if (notNullOrUndefined(fixedDecimals)) { cellData = truncateDecimals(cellData, fixedDecimals) } // Check if the value is larger than the maximum supported value: if (Number.isInteger(cellData) && !Number.isSafeInteger(cellData)) { return getErrorCell( toSafeString(data), "The value is larger than the maximum supported integer values in number columns (2^53)." ) } try { if (useArrowFormatting) { // Use arrow formatting for some selected types (see above) displayData = formatArrowCell(cellData, props.arrowType) } else { displayData = formatNumber( cellData, parameters.format, fixedDecimals ) } } catch (error) { return getErrorCell( toSafeString(cellData), notNullOrUndefined(parameters.format) ? // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Failed to format the number based on the provided format configuration: (${parameters.format}). Error: ${error}` : // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Failed to format the number. Error: ${error}` ) } } return { ...cellTemplate, data: cellData, displayData, isMissingValue: isNullOrUndefined(cellData), // We want to enforce the raw number without formatting when its copied: copyData: isNullOrUndefined(cellData) ? "" : toSafeString(cellData), } as NumberCell }, getCellValue(cell: NumberCell): number | null { return cell.data === undefined ? null : cell.data }, } } NumberColumn.isEditableType = true export default NumberColumn