mirror of
https://github.com/umami-software/umami.git
synced 2026-05-30 06:47:25 +00:00
fix PieChart rendering issues by giving default height.
This commit is contained in:
@@ -13,7 +13,7 @@ ChartJS.defaults.font.family = 'Inter';
|
||||
|
||||
export interface ChartProps extends BoxProps {
|
||||
type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter';
|
||||
chartData?: ChartData & { focusLabel?: string };
|
||||
chartData?: ChartData<any, any, unknown> & { focusLabel?: string };
|
||||
chartOptions?: ChartOptions;
|
||||
updateMode?: UpdateMode;
|
||||
animationDuration?: number;
|
||||
@@ -66,8 +66,6 @@ export function Chart({
|
||||
|
||||
const handleLegendClick = (item: LegendItem) => {
|
||||
if (onLegendClick && type === 'bar') {
|
||||
// Controlled mode: caller owns the hidden state. We report the click
|
||||
// and let the parent push a new hiddenLabels set on the next render.
|
||||
const { datasetIndex } = item;
|
||||
const ds = chart.current.data.datasets[datasetIndex];
|
||||
onLegendClick(ds.label, !hiddenLabels?.has(ds.label));
|
||||
@@ -124,16 +122,11 @@ export function Chart({
|
||||
});
|
||||
}
|
||||
|
||||
// Re-apply caller-driven hidden flags after focusLabel handling so a
|
||||
// dataset stays hidden across data changes (e.g. date-range switches)
|
||||
// even though Chart.js regenerates dataset meta on every replace.
|
||||
if (hiddenLabels) {
|
||||
chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => {
|
||||
if (hiddenLabels.has(ds.label)) {
|
||||
ds.hidden = true;
|
||||
} else if (!chartData.focusLabel) {
|
||||
// Explicitly reset so un-hiding a label is always reflected,
|
||||
// regardless of whether the focusLabel pass ran above.
|
||||
ds.hidden = false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ export interface PieChartProps extends ChartProps {
|
||||
type?: 'doughnut' | 'pie';
|
||||
}
|
||||
|
||||
export function PieChart({ type = 'pie', ...props }: PieChartProps) {
|
||||
export function PieChart({ type = 'pie', height = '300px', ...props }: PieChartProps) {
|
||||
const [tooltip, setTooltip] = useState(null);
|
||||
|
||||
const handleTooltip = ({ tooltip }) => {
|
||||
@@ -24,7 +24,7 @@ export function PieChart({ type = 'pie', ...props }: PieChartProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Chart {...props} type={type} onTooltip={handleTooltip} />
|
||||
<Chart {...props} type={type} height={height} onTooltip={handleTooltip} />
|
||||
{tooltip && <ChartTooltip {...tooltip} />}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -58,16 +58,11 @@ export function DataGrid({
|
||||
const showPager = allowPaging && data && data.count > 0;
|
||||
const { isMobile } = useMobile();
|
||||
const [userDisplayMode, setUserDisplayMode] = useState<DisplayMode | null>(() => {
|
||||
// localStorage can hold anything (extensions, manual edits, schema drift),
|
||||
// so accept only the two values we know how to render and otherwise fall
|
||||
// back to the useMobile-driven default.
|
||||
const stored = getItem(DISPLAY_MODE_STORAGE_KEY);
|
||||
return stored === 'table' || stored === 'cards' ? stored : null;
|
||||
});
|
||||
|
||||
// Effective mode: explicit user choice wins, otherwise fall back to the
|
||||
// mobile-driven default (cards on small viewports, table elsewhere).
|
||||
const displayMode: DisplayMode | undefined = userDisplayMode ?? (isMobile ? 'cards' : undefined);
|
||||
const displayMode: DisplayMode | undefined = isMobile ? 'cards' : userDisplayMode ?? undefined;
|
||||
|
||||
const handleToggleDisplayMode = () => {
|
||||
const next: DisplayMode = displayMode === 'cards' ? 'table' : 'cards';
|
||||
@@ -116,7 +111,7 @@ export function DataGrid({
|
||||
)}
|
||||
<Row alignItems="center" gap style={{ marginLeft: 'auto' }}>
|
||||
{renderActions?.()}
|
||||
{viewToggleButton}
|
||||
{!isMobile && viewToggleButton}
|
||||
</Row>
|
||||
</Row>
|
||||
<LoadingPanel
|
||||
|
||||
@@ -62,13 +62,6 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps)
|
||||
],
|
||||
};
|
||||
} else {
|
||||
// Each label has a preferred palette slot derived from a hash of the
|
||||
// label, so the same event tends to get the same color across reloads
|
||||
// and date-range changes. We walk labels in hash order and, when two
|
||||
// labels prefer the same slot, the later one steps to the next free
|
||||
// slot, so the visible set of <=12 events all get distinct colors.
|
||||
// The right shift on hex6 sidesteps the FNV-1a low-bit bias mod 12
|
||||
// (the FNV prime is close to 2^24).
|
||||
const colorByKey: Record<string, string> = {};
|
||||
const used = new Set<string>();
|
||||
const hashOf = Object.fromEntries(
|
||||
|
||||
@@ -16,11 +16,6 @@ export const IP_ADDRESS_HEADERS = [
|
||||
'x-forwarded',
|
||||
];
|
||||
|
||||
/**
|
||||
* Normalize IP strings to a canonical form:
|
||||
* - strips IPv4-mapped IPv6 (e.g. ::ffff:192.0.2.1 -> 192.0.2.1)
|
||||
* - keeps valid IPv4/IPv6 as-is (canonically formatted by ipaddr.js)
|
||||
*/
|
||||
function normalizeIp(ip?: string | null) {
|
||||
if (!ip) return ip;
|
||||
|
||||
|
||||
@@ -130,9 +130,6 @@ export async function deleteUser(userId: string) {
|
||||
|
||||
const teamIds = teams.map(a => a.id);
|
||||
|
||||
// Cloud mode keeps owned teams (and their team-owned content), so cleanup
|
||||
// only covers user-direct rows. Non-cloud hard-deletes owned teams below,
|
||||
// so we must also clean up team-owned content.
|
||||
const ownedFilter = cloudMode
|
||||
? { userId }
|
||||
: { OR: [{ userId }, { teamId: { in: teamIds } }] };
|
||||
|
||||
Reference in New Issue
Block a user