Skip to content

Commit

Permalink
improve theming colors for flashing and improve flashing component
Browse files Browse the repository at this point in the history
  • Loading branch information
radubrehar committed Oct 10, 2024
1 parent 18ea18b commit 11cb365
Show file tree
Hide file tree
Showing 8 changed files with 276 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { DataSourceApi } from '@infinite-table/infinite-react';
import { useEffect } from 'react';

type Developer = {
birthDate: string;
id: number;
firstName: string;
country: string;
city: string;
currency: string;
email: string;
preferredLanguage: string;
hobby: string;
salary: number;
};

export const TICK_INTERVAL = 100;

let tickingInterval: any | null = null;

function singleUpdate(dataSourceApi: DataSourceApi<Developer>) {
const arr = dataSourceApi.getRowInfoArray();
const len = arr.length;
const randomIndex = Math.floor(Math.random() * len);
const rowInfo = arr[randomIndex];
const id = rowInfo.id;

if (rowInfo.isGroupRow) {
return;
}

const currentData = rowInfo.data;

// generate random signs for the updates for each column
const sign = Math.random() > 0.5 ? 1 : -1;
const currentSalary = currentData.salary;

const randomDelta = Math.round(Math.random() * Math.abs(currentSalary * 0.1));

const newSalary = Math.max(currentSalary + randomDelta * sign, 1000);

const partialData: Partial<Developer> = {
id,
salary: newSalary,
};
dataSourceApi.updateData(partialData);
}
function start(dataSourceApi: DataSourceApi<Developer>) {
tickingInterval = setInterval(() => {
singleUpdate(dataSourceApi);
}, TICK_INTERVAL);
}

function stop() {
if (tickingInterval) {
clearInterval(tickingInterval);
}
}

export function useTickingData(
dataSourceApi: DataSourceApi<Developer> | null,
tick: boolean,
) {
useEffect(() => {
if (!dataSourceApi) {
return;
}

if (tick) {
start(dataSourceApi);
} else {
stop();
}
}, [dataSourceApi, tick]);
}

import {
InfiniteTable,
DataSource,
InfiniteTablePropColumns,
// FlashingColumnCell,
createFlashingColumnCellComponent,
} from '@infinite-table/infinite-react';
import * as React from 'react';

const FlashingColumnCell = createFlashingColumnCellComponent({
flashDuration: 1000,
flashClassName: 'flash-cell',
});
const columns: InfiniteTablePropColumns<Developer> = {
id: {
field: 'id',
},
firstName: {
field: 'firstName',
},
salary: {
field: 'salary',
type: 'number',
defaultEditable: true,
getValueToPersist: ({ value }) => {
return value * 1;
},
components: {
ColumnCell: FlashingColumnCell,
},
},
};

export default function App() {
const [ticking, setTicking] = React.useState(false);
const [dataSourceApi, setDataSourceApi] =
React.useState<DataSourceApi<Developer> | null>(null);

useTickingData(dataSourceApi, ticking);

return (
<>
<div
className={`infinite-theme-mode--dark infinite-theme-name--ocean`}
style={{
display: 'flex',
flexFlow: 'column',
flex: 1,
color: 'var(--infinite-cell-color)',
background: 'var(--infinite-background)',
}}
>
<div style={{ paddingBlock: 10 }}>
<button
onClick={() => {
setTicking((x) => !x);
}}
>
{ticking ? 'Stop' : 'Start'} updates
</button>
</div>

<DataSource<Developer>
data={dataSource}
primaryKey="id"
onReady={setDataSourceApi}
>
<InfiniteTable<Developer>
columns={columns}
domProps={{
style: {
height: '80%',
},
}}
/>
</DataSource>
</div>
</>
);
}

const dataSource = () => {
return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers100-sql?`)
.then((r) => r.json())
.then((data: Developer[]) => {
console.log(data);
return data;
});
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ const defaultRender: FlashingCellOptions['render'] = ({ children }) => {

export const DEFAULT_FLASH_DURATION = 1000;

type FlashDirection = 'up' | 'down' | 'neutral';

const INTERNAL_FLASH_CLS_FOR_DIRECTION: Record<FlashDirection, string[]> = {
up: FlashingColumnCellRecipe({
direction: 'up',
}).split(' '),
down: FlashingColumnCellRecipe({
direction: 'down',
}).split(' '),
neutral: FlashingColumnCellRecipe({
direction: 'neutral',
}).split(' '),
};

export const createFlashingColumnCellComponent = (
options: FlashingCellOptions = {},
) => {
Expand Down Expand Up @@ -70,74 +84,75 @@ export const createFlashingColumnCellComponent = (
initialRef.current = false;

const flashTimeoutIdRef = React.useRef<any>();
const flashDirectionRef = React.useRef<FlashDirection | undefined>();
const fadeTimeoutIdRef = React.useRef<any>();

useEffectWhen(
() => {
if (value === oldValueRef.current) {
return;
}
if (flashTimeoutIdRef.current) {
clearTimeout(flashTimeoutIdRef.current);
}
if (fadeTimeoutIdRef.current) {
clearTimeout(fadeTimeoutIdRef.current);
}

const el = htmlElementRef.current;
const clear = () => {
const el = htmlElementRef.current;

if (flashTimeoutIdRef.current) {
clearTimeout(flashTimeoutIdRef.current);
flashTimeoutIdRef.current = undefined;
}
if (fadeTimeoutIdRef.current) {
clearTimeout(fadeTimeoutIdRef.current);
fadeTimeoutIdRef.current = undefined;
}

if (flashDirectionRef.current && el) {
const flashDirection = flashDirectionRef.current;

const internalflashCls =
INTERNAL_FLASH_CLS_FOR_DIRECTION[flashDirection];

el.classList.remove(
...(flashClassName
? [flashClassName, ...internalflashCls]
: internalflashCls),
);
el.style.removeProperty(currentFlashingDurationVar);

flashDirectionRef.current = undefined;
}
};

oldValueRef.current = value;

const el = htmlElementRef.current;
if (!el) {
return;
}

const flashDirection =
clear();

const flashDirection: FlashDirection =
typeof value === 'number'
? value > oldValue
? 'up'
: 'down'
: 'neutral';

const internalflashCls = FlashingColumnCellRecipe({
direction: flashDirection,
}).split(' ');
const internalflashCls =
INTERNAL_FLASH_CLS_FOR_DIRECTION[flashDirection];

el.style.setProperty(currentFlashingDurationVar, `${duration}`);
if (flashClassName) {
el.classList.add(flashClassName);
}

el.classList.add(...internalflashCls);

flashTimeoutIdRef.current = setTimeout(() => {
flashTimeoutIdRef.current = undefined;

if (flashClassName) {
el.classList.remove(flashClassName);
}
el.classList.remove(...internalflashCls);
el.style.removeProperty(currentFlashingDurationVar);
// if (!fadeDuration || !fadeClassName) {
// return;
// }

// el.classList.add(fadeClassName);
el.classList.add(
...(flashClassName
? [flashClassName, ...internalflashCls]
: internalflashCls),
);

// fadeTimeoutIdRef.current = setTimeout(() => {
// el.classList.remove(fadeClassName);
// fadeTimeoutIdRef.current = undefined;
// }, fadeDuration);
}, duration);
flashDirectionRef.current = flashDirection;
flashTimeoutIdRef.current = setTimeout(clear, duration);

return () => {
if (flashTimeoutIdRef.current) {
clearTimeout(flashTimeoutIdRef.current);
}
if (fadeTimeoutIdRef.current) {
clearTimeout(fadeTimeoutIdRef.current);
}
};
return clear;
},
{
same: [columnId, rowId],
Expand Down
1 change: 1 addition & 0 deletions source/src/components/InfiniteTable/components/cell.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export const FlashingColumnCellRecipe = recipe({
'100%': { opacity: 0 },
}),
),
animationFillMode: 'forwards',

animationDuration: `calc(1ms * ${fallbackVar(
InternalVars.currentFlashingDuration,
Expand Down
7 changes: 7 additions & 0 deletions source/src/components/InfiniteTable/vars-common.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ThemeVars } from './vars.css';

export const CommonThemeVars = {
[ThemeVars.components.Cell.flashingBackground]: ThemeVars.color.accent,
[ThemeVars.components.Cell.flashingUpBackground]: ThemeVars.color.success,
[ThemeVars.components.Cell.flashingDownBackground]: ThemeVars.color.error,
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ThemeVars } from './vars.css';

import { CommonThemeVars } from './vars-common.css';
const borderColor = '#EDF2F7'; // chakra gray 100
export const MinimalistLightVars = {
...CommonThemeVars,
[ThemeVars.background]: 'white',
[ThemeVars.color.color]: '#2D3748', // chakra gray 700
[ThemeVars.components.Row.background]: 'transparent',
Expand Down
2 changes: 2 additions & 0 deletions source/src/components/InfiniteTable/vars-ocean-dark.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const OceanDarkVars = {
[ThemeVars.background]: '#032c4f',
[ThemeVars.components.Menu.separatorColor]: borderColor,
[ThemeVars.color.color]: '#96a0aa',
[ThemeVars.color.success]: '#176417',
[ThemeVars.color.error]: '#5e1414',
[ThemeVars.components.Cell.borderTop]: `1px solid ${borderColor}`,
[ThemeVars.components.HeaderCell.background]: '#04233d',
[ThemeVars.components.HeaderCell.hoverBackground]: '#021f35',
Expand Down
5 changes: 5 additions & 0 deletions source/src/components/InfiniteTable/vars-ocean-light.css.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { CommonThemeVars } from './vars-common.css';
import { ThemeVars } from './vars.css';
const borderColor = `color-mix(in srgb, transparent, ${ThemeVars.color.color} 10%)`;

export const OceanLightVars = {
...CommonThemeVars,
// TODO we need to implement ocean light theme
[ThemeVars.color.accent]: '#8b5cf6',

[ThemeVars.background]: '#d1e8fc',
[ThemeVars.color.color]: '#04233d',
[ThemeVars.color.success]: '#64ce64',
[ThemeVars.color.error]: '#fc6565',

[ThemeVars.components.HeaderCell.background]: '#7dd3fc', // tw sky
[ThemeVars.components.HeaderCell.hoverBackground]: '#38bdf8',

Expand Down
37 changes: 37 additions & 0 deletions www/content/blog/2024/10/10/how-do-i-flash-cells.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: Flashing column cells in Infinite Table
author: admin
draft: true
---

Flashing cells is an important feature that has been requested by some of our users - both [in public](https://github.com/infinite-table/infinite-react/issues/250) and private conversations.

It's also a very useful addition for DataGrids users that work in the financial industry. Version `5.0.0` of `<InfiniteTable />` shipped flashing and in this blogpost we want to show how to use it.

## Configuring a flashing column.

In order to configure a column to flash its cells when the data changes, you need to specify a custom `ColumnCell` component.

```tsx

import { FlashingColumnCell } from '@infinite-table/infinite-react';

const columns: InfiniteTablePropColumns<Developer> = {
id: {
field: 'id',
},
firstName: {
field: 'firstName',
},
monthlyBonus: {
field: 'monthlyBonus',
components: {
ColumnCell: FlashingColumnCell,
}
},
};
```

`@infinite-table/infinite-react` exports a `FlashingColumnCell` React component that you can pass to the `components.ColumnCell` prop of any column you want to flash.


0 comments on commit 11cb365

Please sign in to comment.