Files
kemal.earth 7fce0a12d9 feat(design-system): first pass at db report chart colours (#46787)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

This is a first draft at introducing semantic colours to our
Observability charts. This moves away from just random colours being
assigned to prop after prop. They're only scoped to the Database reports
right now, but if it flows nice, we can open it up to the other reports
too.

This also aims to tone down some of the harsher colours in our charts,
such as the orange which sometimes can look like a warning metric/prop.

| Before | After |
|--------|--------|
| <img width="839" height="336" alt="Screenshot 2026-06-10 at 09 14 56"
src="https://github.com/user-attachments/assets/222747c5-973b-4165-aa53-df7b93412ad3"
/> | <img width="950" height="341" alt="Screenshot 2026-09-14 at 18 14
47"
src="https://github.com/user-attachments/assets/836f3ddd-4a97-4064-b8cf-3a3b435417ac"
/> |

cc @supabase/design for additional thoughts.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added semantic chart color roles with light and dark theme variants
for consistent visualizations.
* Standardized colors and fills across database, networking, storage,
and connection charts.
  * Maximum-value lines now use configured chart colors when available.
  * Added chart palette reference and stress-test examples.
* Added stacked bar charts, customizable margins, and gradient-filled
line charts.
* Improved multi-series bar chart focus and date-range footer alignment.

* **Documentation**
* Documented the chart palette, theme variants, accessibility guidance,
and usage recommendations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Gildas Garcia <1122076+djhi@users.noreply.github.com>
2026-09-16 09:18:40 +01:00

90 lines
2.8 KiB
TypeScript

import { FilterFn } from '@tanstack/react-table'
import { isAfter, isBefore, isSameDay } from 'date-fns'
import { LEVELS } from './DataTable.constants'
export function formatCompactNumber(value: number) {
if (value >= 100 && value < 1000) {
return value.toString() // Keep the number as is if it's in the hundreds
} else if (value >= 1000 && value < 1000000) {
return (value / 1000).toFixed(1) + 'k' // Convert to 'k' for thousands
} else if (value >= 1000000) {
return (value / 1000000).toFixed(1) + 'M' // Convert to 'M' for millions
} else {
return value.toString() // Optionally handle numbers less than 100 if needed
}
}
export function isArrayOfNumbers(arr: any): arr is number[] {
if (!Array.isArray(arr)) return false
return arr.every((item) => typeof item === 'number')
}
export function isArrayOfDates(arr: any): arr is Date[] {
if (!Array.isArray(arr)) return false
return arr.every((item) => item instanceof Date)
}
export function isArrayOfStrings(arr: any): arr is string[] {
if (!Array.isArray(arr)) return false
return arr.every((item) => typeof item === 'string')
}
export function isArrayOfBooleans(arr: any): arr is boolean[] {
if (!Array.isArray(arr)) return false
return arr.every((item) => typeof item === 'boolean')
}
export const inDateRange: FilterFn<any> = (row, columnId, value) => {
const date = new Date(row.getValue(columnId))
const [start, end] = value as Date[]
if (isNaN(date.getTime())) return false
// if no end date, check if it's the same day
if (!end) return isSameDay(date, start)
return isAfter(date, start) && isBefore(date, end)
}
inDateRange.autoRemove = (val: any) => !Array.isArray(val) || !val.length || !isArrayOfDates(val)
export const arrSome: FilterFn<any> = (row, columnId, filterValue) => {
if (!Array.isArray(filterValue)) return false
return filterValue.some((val) => row.getValue<unknown[]>(columnId) === val)
}
arrSome.autoRemove = (val: any) => !Array.isArray(val) || !val?.length
export function getLevelColor(
value: (typeof LEVELS)[number]
): Record<'text' | 'bg' | 'border', string> {
switch (value) {
case 'success':
return {
text: 'text-muted',
bg: 'bg-[var(--chart-muted)] group-data-[state=selected]/row:bg-foreground-lighter',
border:
'border-[var(--chart-muted)] group-data-[state=selected]/row:border-foreground-lighter',
}
case 'warning':
return {
text: 'text-warning',
bg: 'bg-warning',
border: 'border-warning',
}
case 'error':
return {
text: 'text-destructive',
bg: 'bg-destructive',
border: 'border-destructive',
}
default:
return {
text: 'text-info',
bg: 'bg-info',
border: 'border-info',
}
}
}