first commit
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
李启源
2026-07-20 10:21:30 +08:00
commit ea832229da
75 changed files with 16864 additions and 0 deletions

139
src/components/core/grid.ts Normal file
View File

@@ -0,0 +1,139 @@
import type { WaveformDisplayMode } from '../../types'
export const GRID_MIN_COUNT = 1
export const GRID_MAX_COUNT = 10
export const X_AXIS_BAND = 16
export interface WaveformGridOptions {
rowCount?: number
columnCount?: number
showPagination?: boolean
}
export interface NormalizedWaveformGridOptions {
rowCount: number
columnCount: number
showPagination: boolean
}
export interface GridCellGeometry {
slotIndex: number
row: number
column: number
left: number
top: number
width: number
/** Backward-compatible alias for the actual plot height. */
height: number
plotHeight: number
cellHeight: number
xAxisBand: number
}
const normalizeCount = (value: unknown, fallback: number) => {
const numeric = typeof value === 'number' ? value : Number(value)
if (!Number.isFinite(numeric)) return fallback
return Math.min(GRID_MAX_COUNT, Math.max(GRID_MIN_COUNT, Math.floor(numeric)))
}
export function normalizeGridOptions(options?: WaveformGridOptions): NormalizedWaveformGridOptions {
return {
rowCount: normalizeCount(options?.rowCount, 2),
columnCount: normalizeCount(options?.columnCount, 1),
showPagination: options?.showPagination ?? true,
}
}
export function getPageSize(options: NormalizedWaveformGridOptions): number {
return options.rowCount * options.columnCount
}
export function getPageCount(seriesCount: number, options: NormalizedWaveformGridOptions): number {
return Math.max(1, Math.ceil(Math.max(0, seriesCount) / getPageSize(options)))
}
export function paginateSeries<T>(
series: T[],
page: number,
options: NormalizedWaveformGridOptions,
): T[] {
const pageCount = getPageCount(series.length, options)
const safePage = Math.min(pageCount, Math.max(1, Math.floor(page)))
const start = (safePage - 1) * getPageSize(options)
return series.slice(start, start + getPageSize(options))
}
export function getGridGap(displayMode: WaveformDisplayMode): number {
return displayMode === 'compact' ? 0 : displayMode === 'separated' ? 16 : 14
}
export function resolveGridCellGeometry(
innerWidth: number,
innerHeight: number,
options: NormalizedWaveformGridOptions,
displayMode: WaveformDisplayMode,
slotHasSeries: boolean[] = [],
horizontalGap?: number,
): GridCellGeometry[] {
const defaultGap = getGridGap(displayMode)
const columnGap = Number.isFinite(horizontalGap) ? Math.max(0, horizontalGap as number) : defaultGap
const totalHorizontalGap = Math.max(0, options.columnCount - 1) * columnGap
const axisRows = new Set<number>()
if (displayMode === 'independent') {
for (let row = 0; row < options.rowCount; row += 1) axisRows.add(row)
} else {
for (let column = 0; column < options.columnCount; column += 1) {
for (let slotIndex = getPageSize(options) - 1; slotIndex >= 0; slotIndex -= 1) {
if (slotIndex % options.columnCount !== column) continue
if (slotHasSeries[slotIndex]) {
axisRows.add(Math.floor(slotIndex / options.columnCount))
break
}
}
}
}
const totalVerticalGap = Math.max(0, options.rowCount - 1) * defaultGap
const totalAxisBand = axisRows.size * X_AXIS_BAND
const width = Math.max(1, (innerWidth - totalHorizontalGap) / options.columnCount)
const plotHeight = Math.max(1, (innerHeight - totalVerticalGap - totalAxisBand) / options.rowCount)
return Array.from({ length: getPageSize(options) }, (_, slotIndex) => {
const row = Math.floor(slotIndex / options.columnCount)
const column = slotIndex % options.columnCount
const xAxisBand = axisRows.has(row) ? X_AXIS_BAND : 0
const cellHeight = plotHeight + xAxisBand
const top = Array.from({ length: row }, (_, previousRow) => {
const previousBand = axisRows.has(previousRow) ? X_AXIS_BAND : 0
return plotHeight + previousBand + defaultGap
}).reduce((sum, value) => sum + value, 0)
return {
slotIndex,
row,
column,
left: column * (width + columnGap),
top,
width,
height: plotHeight,
plotHeight,
cellHeight,
xAxisBand,
}
})
}
export function getBottomRowCellIndexes(
cells: Array<GridCellGeometry & { hasSeries?: boolean }>,
columnCount: number,
): Set<number> {
const visible = new Set<number>()
for (let column = 0; column < columnCount; column += 1) {
for (let index = cells.length - 1; index >= 0; index -= 1) {
const cell = cells[index]
if (cell.column === column && cell.hasSeries) {
visible.add(cell.slotIndex)
break
}
}
}
return visible
}