mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-09-07 01:56:53 +00:00
perf(data-table): improve data table layout and badge display (#5460)
* perf(table): use percentage-based column widths
- compute each column's width as a percentage of total column size instead of a fixed pixel value, letting the colgroup scale fluidly with the table container.
* perf(data-table): reduce unnecessary re-renders across table components
- stabilize commitSearchValue in toolbar by reading table/searchKey via refs, eliminating recreation on every parent render
- store onColumnFiltersChange in a ref so debounce effect is not reset when the caller passes a new function reference each render
- wrap DataTableRow in React.memo with a custom comparator that ignores getColumnClassName reference churn
- memoize selectedValues Set in DataTableFacetedFilter and wrap with React.memo to prevent rerenders on unrelated state changes
- cache cell meta reads in CompactRow and FallbackRow to a single pass per row; memoize hasCompactMeta in MobileCardList
- memoize hideable columns list in DataTableViewOptions and colSpan in DataTableView
- remove tableClassName and colgroup from scroll-sync effect deps; cache toolbar button NodeList via useLayoutEffect to avoid per-keydown DOM queries
* perf(data-table): replace scroll-sync split header with CSS sticky
- remove JS scroll-sync effect and event listener between split header and body containers.
- merge separate header/body tables into a single scrollable table element, reducing DOM complexity.
- apply CSS sticky positioning to the header for a simpler, hardware-accelerated freeze effect.
* fix(data-table): replace opacity muted colors with color-mix
- switch from bg-muted/50 and bg-muted/30 to color-mix(in oklch) to produce opaque blended backgrounds that prevent scroll content from showing through pinned cells.
- expose --table-header-bg CSS variable so pinned header cells inherit the exact same computed color as the thead background.
- add group class to TableRow to enable group-hover selectors on pinned cell styles.
* feat(data-table): support column pinning via meta.pinned
- add pinned?: 'left' | 'right' to ColumnMeta so pinning is declared once in the column definition and applies to both header and body automatically
- DataTableView derives pinnedColumns from meta.pinned at runtime, merged with any explicit pinnedColumns prop; explicit entries take precedence
- add header and meta.pinned: 'right' to all actions columns across channels, users, api-keys, redemption-codes, models, deployments, and subscriptions tables
* style(row-actions): align action buttons to leading edge of column
* refactor(data-table): extract BadgeListCell and centralize badge alignment
- add BadgeListCell component to data-table for badge lists with overflow tooltip, replacing duplicated renderLimitedItems helpers in channels, models, and pricing columns
- move StatusBadge -ml-1.5 alignment into the component itself via a table-cell context selector, so callers no longer need manual offset wrappers
- remove the table-cell-level -ml-1.5 selector from TableCell now that alignment is handled by StatusBadge directly
* style(row-actions): offset action buttons to align with column header text
* refactor(data-table): consolidate mobile meta into ColumnMeta declaration
- move mobileTitle, mobileBadge, mobileHidden into the global ColumnMeta augmentation so the type is shared across the project
- remove the local MobileColumnMeta interface and getCellMeta helper from mobile-card-list.tsx
- direct col.columnDef.meta access is now type-safe without explicit casting
* refactor(data-table): simplify column header and meta config
- auto-render string `header` values via DataTableColumnHeader so sortable/non-sortable columns work without boilerplate function wrappers
- promote mobile layout hints (mobileTitle, mobileBadge, mobileHidden) into the global ColumnMeta type, removing the local MobileColumnMeta cast in mobile-card-list
- migrate all column files from `meta: { label }` to top-level `header: t('...')`, cutting ~180 lines of repetitive template code
- ViewOptions and MobileCardList label resolution now reads string header first, then meta.label as fallback
* feat(status-badge): add text and underline display types
- introduce StatusBadgeType ('badge' | 'text' | 'underline') and StatusBadgeTypeContext so ancestors can override rendering without touching call sites
- mobile card field rows now use the text type via context, showing badges as plain colored text instead of pills
- ProviderBadge gains data-slot='provider-badge' to enable targeted CSS resets in compact layouts
- replace the implicit [[data-slot=table-cell]>&]:-ml-1.5 rule with explicit -ml-1.5 at each column call site
* refactor(data-table): simplify table filtering internals
- derive toolbar search state from the active table filter to avoid render-time ref writes.
- extract faceted filter selection updates into a pure helper for clearer single and multi-select behavior.
- split pinned column resolution into focused helpers so explicit and meta pins merge predictably.
Co-authored-by: t0ng7u <dev@aiass.cc>
This commit is contained in:
+32
-4
@@ -22,7 +22,6 @@ import { type LucideIcon } from 'lucide-react'
|
||||
import { stringToColor } from '@/lib/colors'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard'
|
||||
|
||||
export const dotColorMap = {
|
||||
success: 'bg-success',
|
||||
warning: 'bg-warning',
|
||||
@@ -73,12 +72,29 @@ export const textColorMap = {
|
||||
|
||||
export type StatusVariant = keyof typeof dotColorMap
|
||||
|
||||
/** Controls the visual style of the badge.
|
||||
* - `badge` — default pill with background and padding (default)
|
||||
* - `text` — plain text, no background or padding, only color
|
||||
* - `underline`— plain text with a bottom border underline
|
||||
*/
|
||||
export type StatusBadgeType = 'badge' | 'text' | 'underline'
|
||||
|
||||
/** Context that lets ancestor components (e.g. MobileCardList field area)
|
||||
* override the badge type without modifying every call site. */
|
||||
export const StatusBadgeTypeContext = React.createContext<StatusBadgeType>('badge')
|
||||
|
||||
const sizeMap = {
|
||||
sm: 'h-5 gap-1 px-1.5 text-xs leading-none',
|
||||
md: 'h-5 gap-1 px-1.5 text-xs leading-none',
|
||||
lg: 'h-6 gap-1.5 px-2 text-xs leading-none',
|
||||
} as const
|
||||
|
||||
const textSizeMap = {
|
||||
sm: 'gap-1 text-xs leading-none',
|
||||
md: 'gap-1 text-xs leading-none',
|
||||
lg: 'gap-1.5 text-xs leading-none',
|
||||
} as const
|
||||
|
||||
export interface StatusBadgeProps extends Omit<
|
||||
React.HTMLAttributes<HTMLSpanElement>,
|
||||
'children'
|
||||
@@ -94,6 +110,8 @@ export interface StatusBadgeProps extends Omit<
|
||||
copyable?: boolean
|
||||
copyText?: string
|
||||
autoColor?: string
|
||||
/** Visual style. Defaults to 'badge'. Can be overridden via StatusBadgeTypeContext. */
|
||||
type?: StatusBadgeType
|
||||
}
|
||||
|
||||
export function StatusBadge({
|
||||
@@ -107,11 +125,14 @@ export function StatusBadge({
|
||||
copyable = true,
|
||||
copyText,
|
||||
autoColor,
|
||||
type: typeProp,
|
||||
className,
|
||||
onClick,
|
||||
...props
|
||||
}: StatusBadgeProps) {
|
||||
const { copyToClipboard } = useCopyToClipboard()
|
||||
const contextType = React.useContext(StatusBadgeTypeContext)
|
||||
const type = typeProp ?? contextType
|
||||
|
||||
const computedVariant: StatusVariant = autoColor
|
||||
? (stringToColor(autoColor) as StatusVariant)
|
||||
@@ -126,14 +147,21 @@ export function StatusBadge({
|
||||
}
|
||||
|
||||
const content =
|
||||
children ?? (label ? <span className='truncate'>{label}</span> : null)
|
||||
children ??
|
||||
(label ? (
|
||||
<span className='min-w-0 truncate leading-normal'>{label}</span>
|
||||
) : null)
|
||||
|
||||
const isBadge = type === 'badge'
|
||||
|
||||
return (
|
||||
<span
|
||||
data-slot='status-badge'
|
||||
className={cn(
|
||||
'inline-flex w-fit max-w-full shrink-0 items-center rounded-4xl font-medium tracking-normal whitespace-nowrap transition-colors',
|
||||
sizeMap[size ?? 'sm'],
|
||||
'inline-flex w-fit max-w-full shrink-0 items-center font-medium tracking-normal whitespace-nowrap transition-colors',
|
||||
isBadge
|
||||
? cn('rounded-4xl', sizeMap[size ?? 'sm'])
|
||||
: cn(textSizeMap[size ?? 'sm'], type === 'underline' && 'border-b border-current pb-px'),
|
||||
textColorMap[computedVariant],
|
||||
pulse && 'animate-pulse',
|
||||
copyable &&
|
||||
|
||||
Reference in New Issue
Block a user