element-plus
Version:
A Component Library for Vue3.0
227 lines (205 loc) • 5.82 kB
text/typescript
import { isNumber } from '@element-plus/utils/util'
import throwError from '@element-plus/utils/error'
import createGrid from '../builders/buildGrid'
import {
AUTO_ALIGNMENT,
SMART_ALIGNMENT,
START_ALIGNMENT,
CENTERED_ALIGNMENT,
END_ALIGNMENT,
} from '../defaults'
const SCOPE = 'ElFixedSizeGrid'
const FixedSizeGrid = createGrid({
name: 'ElFixedSizeGrid',
getColumnPosition: ({ columnWidth }, index) => [
columnWidth as number,
index * (columnWidth as number),
],
getRowPosition: ({ rowHeight }, index) => [
rowHeight as number,
index * (rowHeight as number),
],
getEstimatedTotalHeight: ({ totalRow, rowHeight }) =>
(rowHeight as number) * totalRow,
getEstimatedTotalWidth: ({ totalColumn, columnWidth }) =>
(columnWidth as number) * totalColumn,
getColumnOffset: (
{ totalColumn, columnWidth, width },
columnIndex,
alignment,
scrollLeft,
_,
scrollBarWidth,
) => {
width = Number(width)
const lastColumnOffset = Math.max(
0,
totalColumn * (columnWidth as number) - width,
)
const maxOffset = Math.min(
lastColumnOffset,
columnIndex * (columnWidth as number),
)
const minOffset = Math.max(
0,
columnIndex * (columnWidth as number) -
width +
scrollBarWidth +
(columnWidth as number),
)
if (alignment === 'smart') {
if (scrollLeft >= minOffset - width && scrollLeft <= maxOffset + width) {
alignment = AUTO_ALIGNMENT
} else {
alignment = CENTERED_ALIGNMENT
}
}
switch (alignment) {
case START_ALIGNMENT:
return maxOffset
case END_ALIGNMENT:
return minOffset
case CENTERED_ALIGNMENT:
const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2)
if (middleOffset < Math.ceil(width / 2)) {
return 0
} else if (middleOffset > lastColumnOffset + Math.floor(width / 2)) {
return lastColumnOffset
} else {
return middleOffset
}
case AUTO_ALIGNMENT:
default:
if (scrollLeft >= minOffset && scrollLeft <= maxOffset) {
return scrollLeft
} else if (minOffset > maxOffset) {
return minOffset
} else if (scrollLeft < minOffset) {
return minOffset
} else {
return maxOffset
}
}
},
getRowOffset: (
{ rowHeight, height, totalRow },
rowIndex,
align,
scrollTop,
_,
scrollBarWidth,
): number => {
height = Number(height)
const lastRowOffset = Math.max(0, totalRow * (rowHeight as number) - height)
const maxOffset = Math.min(lastRowOffset, rowIndex * (rowHeight as number))
const minOffset = Math.max(
0,
rowIndex * (rowHeight as number) -
height +
scrollBarWidth +
(rowHeight as number),
)
if (align === SMART_ALIGNMENT) {
if (scrollTop >= minOffset - height && scrollTop <= maxOffset + height) {
align = AUTO_ALIGNMENT
} else {
align = CENTERED_ALIGNMENT
}
}
switch (align) {
case START_ALIGNMENT:
return maxOffset
case END_ALIGNMENT:
return minOffset
case CENTERED_ALIGNMENT:
const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2)
if (middleOffset < Math.ceil(height / 2)) {
return 0
} else if (middleOffset > lastRowOffset + Math.floor(height / 2)) {
return lastRowOffset
} else {
return middleOffset
}
case AUTO_ALIGNMENT:
default:
if (scrollTop >= minOffset && scrollTop <= maxOffset) {
return scrollTop
} else if (minOffset > maxOffset) {
return minOffset
} else if (scrollTop < minOffset) {
return minOffset
} else {
return maxOffset
}
}
},
getColumnStartIndexForOffset: ({ columnWidth, totalColumn }, scrollLeft) =>
Math.max(
0,
Math.min(
totalColumn - 1,
Math.floor(scrollLeft / (columnWidth as number)),
),
),
getColumnStopIndexForStartIndex: (
{ columnWidth, totalColumn, width },
startIndex: number,
scrollLeft: number,
): number => {
const left = startIndex * (columnWidth as number)
const visibleColumnsCount = Math.ceil(
((width as number) + scrollLeft - left) / (columnWidth as number),
)
return Math.max(
0,
Math.min(
totalColumn - 1,
startIndex + visibleColumnsCount - 1,
),
)
},
getRowStartIndexForOffset: (
{ rowHeight, totalRow },
scrollTop: number,
): number =>
Math.max(
0,
Math.min(totalRow - 1, Math.floor(scrollTop / (rowHeight as number))),
),
getRowStopIndexForStartIndex: (
{ rowHeight, totalRow, height },
startIndex: number,
scrollTop: number,
): number => {
const top = startIndex * (rowHeight as number)
const numVisibleRows = Math.ceil(
((height as number) + scrollTop - top) / (rowHeight as number),
)
return Math.max(
0,
Math.min(
totalRow - 1,
startIndex + numVisibleRows - 1, // -1 is because stop index is inclusive
),
)
},
initCache: () => void 0,
clearCache: true,
validateProps: ({ columnWidth, rowHeight }) => {
if (process.env.NODE_ENV !== 'production') {
if (!isNumber(columnWidth)) {
throwError(SCOPE, `
"columnWidth" must be passed as number,
instead ${typeof columnWidth} was given.
`)
}
if (!isNumber(rowHeight)) {
throwError(SCOPE, `
"columnWidth" must be passed as number,
instead ${typeof rowHeight} was given.
`)
}
}
},
})
export default FixedSizeGrid