diff --git a/src/components/Cell.tsx b/src/components/Cell.tsx index 22a979cf..101ced7c 100644 --- a/src/components/Cell.tsx +++ b/src/components/Cell.tsx @@ -4,6 +4,7 @@ import cx from 'classnames' export const Cell: FC<{ gutter: boolean stickyRight: boolean + stickyFirstColumn?: boolean disabled?: boolean className?: string active?: boolean @@ -14,6 +15,7 @@ export const Cell: FC<{ children, gutter, stickyRight, + stickyFirstColumn, active, disabled, className, @@ -28,6 +30,7 @@ export const Cell: FC<{ disabled && 'dsg-cell-disabled', gutter && active && 'dsg-cell-gutter-active', stickyRight && 'dsg-cell-sticky-right', + stickyFirstColumn && 'dsg-cell-sticky-first', className )} style={{ diff --git a/src/components/DataSheetGrid.tsx b/src/components/DataSheetGrid.tsx index e6af4f2c..c9ec9ccb 100644 --- a/src/components/DataSheetGrid.tsx +++ b/src/components/DataSheetGrid.tsx @@ -70,6 +70,7 @@ export const DataSheetGrid = React.memo( headerRowHeight = typeof rowHeight === 'number' ? rowHeight : 40, gutterColumn, stickyRightColumn, + stickyFirstColumn, rowKey, addRowsComponent: AddRowsComponent = AddRows, createRow = DEFAULT_CREATE_ROW as () => T, @@ -251,6 +252,13 @@ export const DataSheetGrid = React.memo( x = 0 } + if ( + stickyFirstColumn && + event.clientX - outerBoundingClientRect.left <= columnRights[1] + ) { + x = event.clientX - outerBoundingClientRect.left + } + if ( hasStickyRightColumn && outerBoundingClientRect.right - event.clientX <= @@ -276,6 +284,7 @@ export const DataSheetGrid = React.memo( getOuterBoundingClientRect, headerRowHeight, hasStickyRightColumn, + stickyFirstColumn, getRowIndex, ] ) @@ -1779,6 +1788,7 @@ export const DataSheetGrid = React.memo( outerRef={outerRef} columnWidths={columnWidths} hasStickyRightColumn={hasStickyRightColumn} + stickyFirstColumn={stickyFirstColumn} displayHeight={displayHeight} data={data} fullWidth={fullWidth} @@ -1807,6 +1817,7 @@ export const DataSheetGrid = React.memo( headerRowHeight={headerRowHeight} rowHeight={getRowSize} hasStickyRightColumn={hasStickyRightColumn} + stickyFirstColumn={stickyFirstColumn} dataLength={data.length} viewHeight={height} viewWidth={width} diff --git a/src/components/Grid.tsx b/src/components/Grid.tsx index 5cab9303..bbd7eb20 100644 --- a/src/components/Grid.tsx +++ b/src/components/Grid.tsx @@ -18,6 +18,7 @@ export const Grid = ({ innerRef, columnWidths, hasStickyRightColumn, + stickyFirstColumn, displayHeight, headerRowHeight, rowHeight, @@ -43,6 +44,7 @@ export const Grid = ({ innerRef: RefObject columnWidths?: number[] hasStickyRightColumn: boolean + stickyFirstColumn?: boolean displayHeight: number headerRowHeight: number rowHeight: (index: number) => { height: number } @@ -97,9 +99,15 @@ export const Grid = ({ overscan: 1, rangeExtractor: (range) => { const result = defaultRangeExtractor(range) + if (result[0] !== 0) { result.unshift(0) } + + if (stickyFirstColumn && result[1] !== 1) { + result.splice(1, 0, 1) + } + if ( hasStickyRightColumn && result[result.length - 1] !== columns.length - 1 @@ -150,6 +158,7 @@ export const Grid = ({ ({ ( rowHeight, activeCell, hasStickyRightColumn, + stickyFirstColumn, dataLength, viewWidth, viewHeight, @@ -170,7 +171,7 @@ export const SelectionRect = React.memo( })} style={{ top: headerRowHeight, - left: columnWidths[0], + left: columnWidths[0] + (stickyFirstColumn ? columnWidths[1] : 0), height: viewHeight ? viewHeight - headerRowHeight : 0, width: contentWidth && viewWidth @@ -178,12 +179,14 @@ export const SelectionRect = React.memo( columnWidths[0] - (hasStickyRightColumn ? columnWidths[columnWidths.length - 1] - : 0) + : 0) - + (stickyFirstColumn ? columnWidths[1] : 0) : `calc(100% - ${ columnWidths[0] + (hasStickyRightColumn ? columnWidths[columnWidths.length - 1] - : 0) + : 0) + + (stickyFirstColumn ? columnWidths[1] : 0) }px)`, }} /> @@ -232,6 +235,8 @@ export const SelectionRect = React.memo( className={cx('dsg-active-cell', { 'dsg-active-cell-focus': editing, 'dsg-active-cell-disabled': activeCellIsDisabled, + 'dsg-active-cell-sticky-first': + stickyFirstColumn && activeCell.col === 0, })} style={activeCellRect} /> @@ -253,18 +258,30 @@ export const SelectionRect = React.memo( }} /> )} - {expandRowsRect && ( -
- )} {expandRowsIndicator && (
)} + {expandRowsRect && ( +
+ )} ) } diff --git a/src/style.css b/src/style.css index acf3bc9c..59e9d385 100644 --- a/src/style.css +++ b/src/style.css @@ -81,6 +81,14 @@ transform: translateY(-100%); } +.dsg-cell-sticky-first { + position: sticky; + top: 0; + z-index: 20; + margin-right: auto; + transform: translateY(-100%); +} + .dsg-cell-disabled { background: var(--dsg-cell-disabled-background-color); } @@ -101,6 +109,10 @@ box-shadow: 1px 0 var(--dsg-border-color), 0 1px var(--dsg-border-color); } +.dsg-cell-header.dsg-cell-sticky-first { + box-shadow: 1px 0 var(--dsg-border-color), 0 1px var(--dsg-border-color); +} + .dsg-cell-header.dsg-cell-sticky-right { box-shadow: 0 1px var(--dsg-border-color); } @@ -140,6 +152,11 @@ border-color: var(--dsg-selection-disabled-border-color); } +.dsg-active-cell-sticky-first { + position: sticky; + z-index: 40; +} + .dsg-selection-rect { background: var(--dsg-selection-background-color); } @@ -405,6 +422,7 @@ .dsg-selection-col-marker-container { position: absolute; top: 0; + z-index: 20; } .dsg-selection-col-marker { @@ -570,6 +588,11 @@ border: solid 1px var(--dsg-selection-disabled-border-color); } +.dsg-expand-rows-indicator-sticky-first { + position: sticky; + z-index: 40; +} + .dsg-expand-rows-rect { position: absolute; box-sizing: border-box; @@ -577,3 +600,8 @@ pointer-events: none; background: rgba(0, 0, 0, 0.03); } + +.dsg-expand-rows-rect-sticky-first{ + position: sticky; + z-index: 40; +} diff --git a/src/types.ts b/src/types.ts index 768ec556..729bb9ed 100644 --- a/src/types.ts +++ b/src/types.ts @@ -62,6 +62,7 @@ export type SelectionContextType = { selection: Selection | null dataLength: number rowHeight: (index: number) => { height: number; top: number } + stickyFirstColumn?: boolean hasStickyRightColumn: boolean editing: boolean isCellDisabled: (cell: Cell) => boolean @@ -93,7 +94,13 @@ export type AddRowsComponentProps = { export type ContextMenuItem = | { - type: 'INSERT_ROW_BELLOW' | 'DELETE_ROW' | 'DUPLICATE_ROW' | 'COPY' | 'CUT' | 'PASTE' + type: + | 'INSERT_ROW_BELLOW' + | 'DELETE_ROW' + | 'DUPLICATE_ROW' + | 'COPY' + | 'CUT' + | 'PASTE' action: () => void } | { @@ -135,6 +142,7 @@ export type DataSheetGridProps = { columns?: Partial>[] gutterColumn?: SimpleColumn | false stickyRightColumn?: SimpleColumn + stickyFirstColumn?: boolean rowKey?: string | ((opts: { rowData: T; rowIndex: number }) => string) height?: number rowHeight?: number | ((opt: { rowData: T; rowIndex: number }) => number)