Skip to content

Commit

Permalink
Pinning feature Logtable added
Browse files Browse the repository at this point in the history
  • Loading branch information
kartik-gupta-ij committed Aug 1, 2023
1 parent bb1b582 commit 116e8c4
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 25 deletions.
34 changes: 25 additions & 9 deletions src/pages/Logs/Column.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Log, SortOrder } from '@/@types/parseable/api/query';
import { Box, Checkbox, Popover, TextInput, Tooltip, UnstyledButton, px } from '@mantine/core';
import { type ChangeEvent, type FC, Fragment, useTransition, useRef, useCallback, useMemo } from 'react';
import { type ChangeEvent, type FC, Fragment, useTransition, useRef, useCallback, useMemo, useEffect } from 'react';
import { IconDotsVertical, IconFilter, IconSearch, IconSortAscending, IconSortDescending } from '@tabler/icons-react';
import useMountedState from '@/hooks/useMountedState';
import { useTableColumnStyle } from './styles';
Expand Down Expand Up @@ -65,17 +65,21 @@ type Column = {
applyFilter: (columnName: string, value: string[]) => void;
setSorting: (order: SortOrder | null) => void;
fieldSortOrder: SortOrder | null;
isColumnPinned: (columnName: string) => boolean;
logsSchema: any;
};

const Column: FC<Column> = (props) => {
const { columnName, getColumnFilters, appliedFilter, applyFilter, setSorting, fieldSortOrder } = props;
const { columnName, getColumnFilters, appliedFilter, applyFilter, setSorting, fieldSortOrder ,isColumnPinned, logsSchema} = props;

// columnValues ref will always have the unfiltered data.
const _columnValuesRef = useRef<Log[number][] | null>(null);

const [columnValues, setColumnValues] = useMountedState<Log[number][] | null>(null);
const [selectedFilters, setSelectedFilters] = useMountedState<string[]>(appliedFilter(columnName));
const [isPending, startTransition] = useTransition();
const ref = useRef<HTMLTableCellElement>(null);
const [ leftOffset, setLeftOffset ] = useMountedState(0);

const onSearch = (e: ChangeEvent<HTMLInputElement>) => {
const search = e.target.value.trim();
Expand Down Expand Up @@ -115,21 +119,33 @@ const Column: FC<Column> = (props) => {
function capitalizeFirstLetter(word: string) {
return word.charAt(0).toUpperCase() + word.slice(1);
}
const { classes, cx } = useTableColumnStyle();
const { labelBtn, applyBtn, labelIcon, labelIconActive, searchInputStyle,filterText } = classes;
useEffect(() => {
if (ref.current) {
setLeftOffset(ref.current.offsetLeft);
}
}, [ref.current, logsSchema,isColumnPinned(columnName)]);
const { classes } = useTableColumnStyle();
const { labelBtn, applyBtn, labelIcon, searchInputStyle,filterText } = classes;

return (
<th>
<th
{...(isColumnPinned(columnName) ? { style: { left: `${leftOffset}px`,position:"sticky" } } : {})}
ref={isColumnPinned(columnName) ? ref : null}
>
<Popover position="bottom" withArrow withinPortal shadow="md" zIndex={1} onOpen={onOpen}>
<Popover.Target>
<UnstyledButton className={labelBtn}>
<span className="label">{capitalizeFirstLetter(columnName)}</span>
<IconDotsVertical
stroke={filterActive ? 3 : 1.8}
stroke={ 1.8}
size={px('1rem')}
className={labelIcon}
/>
<IconFilter
stroke={ 1.8}
size={px('1rem')}
className={cx(labelIcon, {
[labelIconActive]: filterActive,
})}
className={labelIcon}
display={filterActive ? '' : 'none'}
/>
</UnstyledButton>
</Popover.Target>
Expand Down
61 changes: 50 additions & 11 deletions src/pages/Logs/LogRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { LogStreamData } from '@/@types/parseable/api/stream';
import { parseLogData } from '@/utils';
import { Box, px } from '@mantine/core';
import { IconArrowNarrowRight } from '@tabler/icons-react';
import { FC, Fragment } from 'react';
import { FC, Fragment, useEffect, useRef } from 'react';
import { useLogsPageContext } from './Context';
import { useLogTableStyles } from './styles';
import { Log } from '@/@types/parseable/api/query';
Expand All @@ -13,10 +13,11 @@ type LogRowProps = {
logData: Log[];
logsSchema: LogStreamData;
isColumnActive: (columnName: string) => boolean;
isColumnPinned: (columnName: string) => boolean;
};

const LogRow: FC<LogRowProps> = (props) => {
const { logData, logsSchema, isColumnActive } = props;
const { logData, logsSchema, isColumnActive,isColumnPinned } = props;
const {
state: { subViewLog },
} = useLogsPageContext();
Expand All @@ -26,7 +27,9 @@ const LogRow: FC<LogRowProps> = (props) => {
};

const { classes } = useLogTableStyles();
const { trStyle, trEvenStyle } = classes;
const { trStyle } = classes;



return (
<Fragment>
Expand All @@ -38,14 +41,12 @@ const LogRow: FC<LogRowProps> = (props) => {
Hopefully there will be a plan to add a p_id filed internally
For now index is a better option for uniqueness, if you have a better way to handle this let us know
*/
<tr key={logIndex} className={logIndex % 2 ? trStyle : trEvenStyle} onClick={() => onShow(log)}>
{logsSchema.map((logSchema, logSchemaIndex) => {
if (!isColumnActive(logSchema.name) || skipFields.includes(logSchema.name)) return null;

return (
<td key={`${logSchema.name}-${logSchemaIndex}`}>{parseLogData(log[logSchema.name], logSchema.name)}</td>
);
})}
<tr key={logIndex} className={trStyle} onClick={() => onShow(log)}>
{logsSchema.map((logSchema, logSchemaIndex) => (
<TdText log={log} logSchema={logSchema} logSchemaIndex={logSchemaIndex} isColumnActive={isColumnActive} isColumnPinned={isColumnPinned}
logsSchema={logsSchema}
/>
))}
<TdArrow />
</tr>
);
Expand All @@ -67,4 +68,42 @@ const TdArrow: FC = () => {
);
};


type TdTextProps = {
log: Log;
logSchema: any;
logSchemaIndex: number;
isColumnActive: (columnName: string) => boolean;
isColumnPinned: (columnName: string) => boolean;
logsSchema: LogStreamData;
};

const TdText: FC<TdTextProps> = (props) => {

const { log, logSchema, logSchemaIndex, isColumnActive,isColumnPinned , logsSchema} = props;
const ref = useRef<HTMLTableCellElement>(null);

useEffect(() => {
if (ref.current) {
ref.current.style.left = `${ref.current.offsetLeft}px`;
ref.current.style.position = 'sticky';
ref.current.style.backgroundColor = "inherit";

}
}, [ref.current, logsSchema]);

if (!isColumnActive(logSchema.name) || skipFields.includes(logSchema.name)) return null;

return (
<td key={`${logSchema.name}-${logSchemaIndex}`}
ref={isColumnPinned(logSchema.name) ? ref : null}

> {parseLogData(log[logSchema.name], logSchema.name)}

</td>
);
};



export default LogRow;
55 changes: 51 additions & 4 deletions src/pages/Logs/LogTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import LogRow from './LogRow';
import { useLogTableStyles } from './styles';
import useMountedState from '@/hooks/useMountedState';
import ErrorText from '@/components/Text/ErrorText';
import { IconDotsVertical, IconSelector, IconGripVertical } from '@tabler/icons-react';
import { IconDotsVertical, IconSelector, IconGripVertical, IconPinned } from '@tabler/icons-react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { Field } from '@/@types/parseable/dataType';
import EmptyBox from '@/components/Empty';
Expand All @@ -34,6 +34,7 @@ const LogTable: FC = () => {
const [refreshInterval, setRefreshInterval] = useMountedState<number | null>(null);
const [logStreamError, setLogStreamError] = useMountedState<string | null>(null);
const [columnToggles, setColumnToggles] = useMountedState<Map<string, boolean>>(new Map());
const [columnPinned, setColumnPinned] = useMountedState<Map<string, boolean>>(new Map());
const {
data: logsSchema,
getDataSchema,
Expand Down Expand Up @@ -79,6 +80,21 @@ const LogTable: FC = () => {
[columnToggles],
);

const isColumnPinned = useCallback(
(columnName: string) => {
if (!columnPinned.has(columnName)) return false;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return columnPinned.get(columnName)!;
},
[columnPinned],
);

const toggleColumnPinned = (columnName: string, value: boolean) => {

setColumnPinned(new Map(columnPinned.set(columnName, value)));
};

const toggleColumn = (columnName: string, value: boolean) => {
setColumnToggles(new Map(columnToggles.set(columnName, value)));
};
Expand Down Expand Up @@ -186,6 +202,8 @@ const LogTable: FC = () => {
getColumnFilters={getColumnFilters}
setSorting={sortingSetter(field.name)}
fieldSortOrder={sort.field === field.name ? sort.order : null}
isColumnPinned={isColumnPinned}
logsSchema={logsSchema}
/>
);
});
Expand Down Expand Up @@ -215,7 +233,7 @@ const LogTable: FC = () => {
Boolean(logsSchema?.fields.length) && Boolean(pageLogData?.data.length) ? (
<Box className={innerContainer}>
<ScrollArea className={tableContainer} type="always">
<Table className={tableStyle}>
<Table className={tableStyle} striped>
<Thead className={theadStyle}>
{renderTh}
<ThColumnMenu
Expand All @@ -224,13 +242,16 @@ const LogTable: FC = () => {
toggleColumn={toggleColumn}
isColumnActive={isColumnActive}
reorderColumn={reorderSchemaFields}
isColumnPinned={isColumnPinned}
toggleColumnPinned={toggleColumnPinned}
/>
</Thead>
<Tbody>
<LogRow
logData={pageLogData?.data || []}
logsSchema={logsSchema?.fields || []}
isColumnActive={isColumnActive}
isColumnPinned={isColumnPinned}
/>
</Tbody>
</Table>
Expand Down Expand Up @@ -269,15 +290,29 @@ const LogTable: FC = () => {
type ThColumnMenuItemProps = {
field: Field;
index: number;
lastIndex: number;
toggleColumn: (columnName: string, value: boolean) => void;
isColumnActive: (columnName: string) => boolean;
toggleColumnPinned: (columnName: string, value: boolean) => void;
isColumnPinned: (columnName: string) => boolean;
reOrderColumn: (destination: number, source: number) => void;
};

const ThColumnMenuItem: FC<ThColumnMenuItemProps> = (props) => {
const { field, index, toggleColumn, isColumnActive } = props;
const { field, index, toggleColumn, isColumnActive , toggleColumnPinned,isColumnPinned, reOrderColumn, lastIndex} = props;
const { classes } = useLogTableStyles();
if (skipFields.includes(field.name)) return null;

const togglePin = () => {
if(isColumnPinned(field.name)){
reOrderColumn(lastIndex, index);
}
else{
reOrderColumn(0, index);
}
toggleColumnPinned(field.name, !isColumnPinned(field.name))

}
return (
<Draggable key={field.name} index={index} draggableId={field.name}>
{(provided) => (
Expand All @@ -296,6 +331,12 @@ const ThColumnMenuItem: FC<ThColumnMenuItemProps> = (props) => {
checked={isColumnActive(field.name)}
onChange={(event) => toggleColumn(field.name, event.currentTarget.checked)}
/>
<div className={classes.thColumnMenuPinHandle} >
<IconPinned size="1.05rem" stroke={isColumnPinned(field.name) ? 2.5 : 1.5 }
onClick={togglePin}
/>
</div>

</div>
</Menu.Item>
)}
Expand All @@ -309,10 +350,12 @@ type ThColumnMenuProps = {
toggleColumn: (columnName: string, value: boolean) => void;
reorderColumn: (destination: number, source: number) => void;
isColumnActive: (columnName: string) => boolean;
isColumnPinned: (columnName: string) => boolean;
toggleColumnPinned: (columnName: string, value: boolean) => void;
};

const ThColumnMenu: FC<ThColumnMenuProps> = (props) => {
const { logSchemaFields, isColumnActive, toggleColumn, reorderColumn } = props;
const { logSchemaFields, isColumnActive, toggleColumn, reorderColumn, isColumnPinned ,toggleColumnPinned} = props;

const { classes } = useLogTableStyles();
const { thColumnMenuBtn, thColumnMenuDropdown } = classes;
Expand Down Expand Up @@ -342,6 +385,10 @@ const ThColumnMenu: FC<ThColumnMenuProps> = (props) => {
index={index}
toggleColumn={toggleColumn}
isColumnActive={isColumnActive}
isColumnPinned={isColumnPinned}
toggleColumnPinned={toggleColumnPinned}
reOrderColumn={reorderColumn}
lastIndex={logSchemaFields.length - 1}
/>
);
})}
Expand Down
21 changes: 20 additions & 1 deletion src/pages/Logs/styles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ export const useLogTableStyles = createStyles((theme) => {
background: colors.gray[1],
},
},
tdPinned: {
position: 'sticky',
left: 0,
background: 'inherit',
boxShadow: shadows.sm,
'tr:hover &': {
background: colors.gray[1],
},
},

thColumnMenuBtn: {
width: widths[10],
Expand All @@ -150,6 +159,15 @@ export const useLogTableStyles = createStyles((theme) => {
color: theme.colorScheme === 'dark' ? theme.colors.dark[1] : theme.colors.gray[6],
paddingRight: theme.spacing.md,
},
thColumnMenuPinHandle: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
height: '100%',
width: '100%',
color: theme.colorScheme === 'dark' ? theme.colors.dark[1] : theme.colors.gray[6],
paddingRight: theme.spacing.md,
},

thColumnMenuDraggable: {
display: 'flex',
Expand Down Expand Up @@ -342,6 +360,7 @@ export const useTableColumnStyle = createStyles((theme) => {
color: sColor,
},

}
},

};
});

0 comments on commit 116e8c4

Please sign in to comment.