Table: Fix some styling issues with frozen columns (#109425)
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
getLuminance,
|
||||
lighten,
|
||||
asRgbString,
|
||||
onBackground,
|
||||
} from './colorManipulator';
|
||||
|
||||
describe('utils/colorManipulator', () => {
|
||||
@@ -423,4 +424,16 @@ describe('utils/colorManipulator', () => {
|
||||
expect(asRgbString('#000000')).toEqual('rgb(0, 0, 0)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('onBackground', () => {
|
||||
it('should calculate the foreground color given a background color', () => {
|
||||
expect(onBackground('#ffffff', '#000').toHexString()).toBe('#ffffff');
|
||||
expect(onBackground('#ffffff00', '#000').toHexString()).toBe('#000000');
|
||||
expect(onBackground('#ffffff77', '#000').toHexString()).toBe('#777777');
|
||||
expect(onBackground('#262a6d82', '#644242').toHexString()).toBe('#443658');
|
||||
expect(onBackground('rgba(255,0,0,0.5)', 'rgba(0,255,0,0.5)').toRgbString()).toBe('rgba(170, 85, 0, 0.75)');
|
||||
expect(onBackground('rgba(255,0,0,0.5)', 'rgba(0,0,255,1)').toRgbString()).toBe('rgb(128, 0, 128)');
|
||||
expect(onBackground('rgba(0,0,255,1)', 'rgba(0,0,0,0.5)').toRgbString()).toBe('rgb(0, 0, 255)');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -366,6 +366,32 @@ export function lighten(color: string, coefficient: number) {
|
||||
return recomposeColor(parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* given foreground and background colors, returns the color of the foreground color on the background color.
|
||||
* this is valuable for foreground colors with alpha.
|
||||
*
|
||||
* adapted from https://github.com/scttcper/tinycolor/blob/2927a9d2aa03e037486a79a295542a7848621691/src/index.ts#L583-L594
|
||||
*
|
||||
* @param foreground
|
||||
* @param background
|
||||
* @returns a tinycolor instance
|
||||
*/
|
||||
export const onBackground = (
|
||||
foreground: tinycolor.ColorInput,
|
||||
background: tinycolor.ColorInput
|
||||
): tinycolor.Instance => {
|
||||
const fg = tinycolor(foreground).toRgb();
|
||||
const bg = tinycolor(background).toRgb();
|
||||
const alpha = fg.a + bg.a * (1 - fg.a);
|
||||
|
||||
return tinycolor({
|
||||
r: (fg.r * fg.a + bg.r * bg.a * (1 - fg.a)) / alpha,
|
||||
g: (fg.g * fg.a + bg.g * bg.a * (1 - fg.a)) / alpha,
|
||||
b: (fg.b * fg.a + bg.b * bg.a * (1 - fg.a)) / alpha,
|
||||
a: alpha,
|
||||
});
|
||||
};
|
||||
|
||||
interface DecomposeColor {
|
||||
type: string;
|
||||
values: any;
|
||||
@@ -387,4 +413,5 @@ export const colorManipulator = {
|
||||
alpha,
|
||||
darken,
|
||||
lighten,
|
||||
onBackground,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { Property } from 'csstype';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { GrafanaTheme2, colorManipulator } from '@grafana/data';
|
||||
|
||||
import { COLUMN, TABLE } from './constants';
|
||||
import { TableCellStyles } from './types';
|
||||
@@ -10,109 +10,120 @@ import { getJustifyContent } from './utils';
|
||||
export const getGridStyles = (
|
||||
theme: GrafanaTheme2,
|
||||
{ enablePagination, transparent }: { enablePagination?: boolean; transparent?: boolean }
|
||||
) => ({
|
||||
grid: css({
|
||||
'--rdg-background-color': transparent ? theme.colors.background.canvas : theme.colors.background.primary,
|
||||
'--rdg-header-background-color': transparent ? theme.colors.background.canvas : theme.colors.background.primary,
|
||||
'--rdg-border-color': theme.colors.border.weak,
|
||||
'--rdg-color': theme.colors.text.primary,
|
||||
'--rdg-summary-border-color': theme.colors.border.weak,
|
||||
'--rdg-summary-border-width': '1px',
|
||||
) => {
|
||||
const bgColor = transparent ? theme.colors.background.canvas : theme.colors.background.primary;
|
||||
// this needs to be pre-calc'd since the theme colors have alpha and the border color becomes
|
||||
// unpredictable for background color cells
|
||||
const borderColor = colorManipulator.onBackground(theme.colors.border.weak, bgColor).toHexString();
|
||||
|
||||
// note: this cannot have any transparency since default cells that
|
||||
// overlay/overflow on hover inherit this background and need to occlude cells below
|
||||
'--rdg-row-background-color': transparent ? theme.colors.background.canvas : theme.colors.background.primary,
|
||||
'--rdg-row-hover-background-color': transparent
|
||||
? theme.colors.background.primary
|
||||
: theme.colors.background.secondary,
|
||||
return {
|
||||
grid: css({
|
||||
'--rdg-background-color': bgColor,
|
||||
'--rdg-header-background-color': bgColor,
|
||||
'--rdg-border-color': borderColor,
|
||||
'--rdg-color': theme.colors.text.primary,
|
||||
'--rdg-summary-border-color': borderColor,
|
||||
'--rdg-summary-border-width': '1px',
|
||||
|
||||
// TODO: magic 32px number is unfortunate. it would be better to have the content
|
||||
// flow using flexbox rather than hard-coding this size via a calc
|
||||
blockSize: enablePagination ? 'calc(100% - 32px)' : '100%',
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: theme.isDark ? '#fff5 #fff1' : '#0005 #0001',
|
||||
// note: this cannot have any transparency since default cells that
|
||||
// overlay/overflow on hover inherit this background and need to occlude cells below
|
||||
'--rdg-row-background-color': bgColor,
|
||||
'--rdg-row-hover-background-color': transparent
|
||||
? theme.colors.background.primary
|
||||
: theme.colors.background.secondary,
|
||||
|
||||
border: 'none',
|
||||
// TODO: magic 32px number is unfortunate. it would be better to have the content
|
||||
// flow using flexbox rather than hard-coding this size via a calc
|
||||
blockSize: enablePagination ? 'calc(100% - 32px)' : '100%',
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: theme.isDark ? '#fff5 #fff1' : '#0005 #0001',
|
||||
|
||||
'.rdg-cell': {
|
||||
padding: TABLE.CELL_PADDING,
|
||||
border: 'none',
|
||||
|
||||
'&:last-child': {
|
||||
borderInlineEnd: 'none',
|
||||
},
|
||||
},
|
||||
|
||||
// add a box shadow on hover and selection for all body cells
|
||||
'& > :not(.rdg-summary-row, .rdg-header-row) > .rdg-cell': {
|
||||
'&:hover, &[aria-selected=true]': { boxShadow: theme.shadows.z2 },
|
||||
// selected cells should appear below hovered cells.
|
||||
'&:hover': { zIndex: theme.zIndex.tooltip - 4 },
|
||||
'&[aria-selected=true]': { zIndex: theme.zIndex.tooltip - 5 },
|
||||
},
|
||||
|
||||
'.rdg-cell.rdg-cell-frozen': { zIndex: theme.zIndex.tooltip - 2 },
|
||||
|
||||
'.rdg-header-row, .rdg-summary-row': {
|
||||
'.rdg-cell': {
|
||||
zIndex: theme.zIndex.tooltip - 3,
|
||||
padding: TABLE.CELL_PADDING,
|
||||
|
||||
'&.rdg-cell-frozen': {
|
||||
zIndex: theme.zIndex.tooltip - 1,
|
||||
'&:last-child': {
|
||||
borderInlineEnd: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
gridNested: css({
|
||||
height: '100%',
|
||||
width: `calc(100% - ${COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING * 2 - 1}px)`,
|
||||
overflow: 'visible',
|
||||
marginLeft: COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING - 1,
|
||||
marginBlock: TABLE.CELL_PADDING,
|
||||
}),
|
||||
cellNested: css({ '&[aria-selected=true]': { outline: 'none' } }),
|
||||
noDataNested: css({
|
||||
height: TABLE.NESTED_NO_DATA_HEIGHT,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: theme.colors.text.secondary,
|
||||
fontSize: theme.typography.h4.fontSize,
|
||||
}),
|
||||
cellActions: css({
|
||||
display: 'none',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
margin: 'auto',
|
||||
height: '100%',
|
||||
color: theme.colors.text.primary,
|
||||
background: theme.isDark ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)',
|
||||
padding: theme.spacing.x0_5,
|
||||
paddingInlineStart: theme.spacing.x1,
|
||||
}),
|
||||
cellActionsEnd: css({ left: 0 }),
|
||||
cellActionsStart: css({ right: 0 }),
|
||||
headerRow: css({
|
||||
paddingBlockStart: 0,
|
||||
fontWeight: 'normal',
|
||||
'& .rdg-cell': { height: '100%', alignItems: 'flex-end' },
|
||||
}),
|
||||
displayNone: css({ display: 'none' }),
|
||||
paginationContainer: css({
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: '8px',
|
||||
width: '100%',
|
||||
}),
|
||||
paginationSummary: css({
|
||||
color: theme.colors.text.secondary,
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: theme.spacing(0, 1, 0, 2),
|
||||
}),
|
||||
menuItem: css({ maxWidth: '200px' }),
|
||||
});
|
||||
|
||||
// add a box shadow on hover and selection for all body cells
|
||||
'& > :not(.rdg-summary-row, .rdg-header-row) > .rdg-cell': {
|
||||
'&:hover, &[aria-selected=true]': { boxShadow: theme.shadows.z2 },
|
||||
// selected cells should appear below hovered cells.
|
||||
'&:hover': { zIndex: theme.zIndex.tooltip - 7 },
|
||||
'&[aria-selected=true]': { zIndex: theme.zIndex.tooltip - 6 },
|
||||
},
|
||||
|
||||
'.rdg-cell.rdg-cell-frozen': {
|
||||
backgroundColor: '--rdg-row-background-color',
|
||||
zIndex: theme.zIndex.tooltip - 4,
|
||||
'&:hover': { zIndex: theme.zIndex.tooltip - 2 },
|
||||
'&[aria-selected=true]': { zIndex: theme.zIndex.tooltip - 3 },
|
||||
},
|
||||
|
||||
'.rdg-header-row, .rdg-summary-row': {
|
||||
'.rdg-cell': {
|
||||
zIndex: theme.zIndex.tooltip - 5,
|
||||
'&.rdg-cell-frozen': {
|
||||
zIndex: theme.zIndex.tooltip - 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
gridNested: css({
|
||||
height: '100%',
|
||||
width: `calc(100% - ${COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING * 2 - 1}px)`,
|
||||
overflow: 'visible',
|
||||
marginLeft: COLUMN.EXPANDER_WIDTH - TABLE.CELL_PADDING - 1,
|
||||
marginBlock: TABLE.CELL_PADDING,
|
||||
}),
|
||||
cellNested: css({ '&[aria-selected=true]': { outline: 'none' } }),
|
||||
noDataNested: css({
|
||||
height: TABLE.NESTED_NO_DATA_HEIGHT,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: theme.colors.text.secondary,
|
||||
fontSize: theme.typography.h4.fontSize,
|
||||
}),
|
||||
cellActions: css({
|
||||
display: 'none',
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
margin: 'auto',
|
||||
height: '100%',
|
||||
color: theme.colors.text.primary,
|
||||
background: theme.isDark ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)',
|
||||
padding: theme.spacing.x0_5,
|
||||
paddingInlineStart: theme.spacing.x1,
|
||||
}),
|
||||
cellActionsEnd: css({ left: 0 }),
|
||||
cellActionsStart: css({ right: 0 }),
|
||||
headerRow: css({
|
||||
paddingBlockStart: 0,
|
||||
fontWeight: 'normal',
|
||||
'& .rdg-cell': { height: '100%', alignItems: 'flex-end' },
|
||||
}),
|
||||
displayNone: css({ display: 'none' }),
|
||||
paginationContainer: css({
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginTop: '8px',
|
||||
width: '100%',
|
||||
}),
|
||||
paginationSummary: css({
|
||||
color: theme.colors.text.secondary,
|
||||
fontSize: theme.typography.bodySmall.fontSize,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
padding: theme.spacing(0, 1, 0, 2),
|
||||
}),
|
||||
menuItem: css({ maxWidth: '200px' }),
|
||||
};
|
||||
};
|
||||
|
||||
export const getFooterStyles = (justifyContent: Property.JustifyContent) => ({
|
||||
footerCellCountRows: css({ display: 'flex', justifyContent: 'space-between' }),
|
||||
@@ -135,7 +146,6 @@ export const getDefaultCellStyles: TableCellStyles = (theme, { textAlign, should
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
textAlign,
|
||||
backgroundClip: 'padding-box !important', // helps when cells have a bg color
|
||||
justifyContent: getJustifyContent(textAlign),
|
||||
...(shouldOverflow && { minHeight: '100%' }),
|
||||
'&:hover, &[aria-selected=true]': {
|
||||
|
||||
Reference in New Issue
Block a user