mirror of
https://github.com/kremalicious/umami.git
synced 2025-02-06 01:15:42 +01:00
Updated avatar colors and events table.
This commit is contained in:
parent
9c32057841
commit
3262ea0285
@ -2,7 +2,7 @@ import { GridTable, GridColumn } from 'react-basics';
|
||||
import { useLocale, useMessages } from 'components/hooks';
|
||||
import Empty from 'components/common/Empty';
|
||||
import { formatDistance } from 'date-fns';
|
||||
import Profile from 'components/common/Profile';
|
||||
import Avatar from 'components/common/Avatar';
|
||||
import Link from 'next/link';
|
||||
|
||||
export function EventsTable({ data = [] }) {
|
||||
@ -15,18 +15,23 @@ export function EventsTable({ data = [] }) {
|
||||
|
||||
return (
|
||||
<GridTable data={data}>
|
||||
<GridColumn name="id" label="ID" />
|
||||
<GridColumn name="session" label={formatMessage(labels.session)}>
|
||||
<GridColumn name="session" label={formatMessage(labels.session)} width={'100px'}>
|
||||
{row => (
|
||||
<Link href={`/sessions/`}>
|
||||
<Profile seed={row.sessionId} size={64} />
|
||||
<Avatar seed={row.sessionId} size={64} />
|
||||
</Link>
|
||||
)}
|
||||
</GridColumn>
|
||||
<GridColumn name="eventName" label={formatMessage(labels.event)}>
|
||||
{row => formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
||||
<GridColumn name="event" label={formatMessage(labels.event)}>
|
||||
{row => {
|
||||
return (
|
||||
<>
|
||||
{formatMessage(row.eventName ? labels.triggeredEvent : labels.viewedPage)}
|
||||
<strong>{row.eventName}</strong>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</GridColumn>
|
||||
<GridColumn name="eventName" label={formatMessage(labels.name)} />
|
||||
<GridColumn name="urlPath" label={formatMessage(labels.path)} />
|
||||
<GridColumn name="created" label={formatMessage(labels.created)}>
|
||||
{row =>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Link from 'next/link';
|
||||
import { GridColumn, GridTable, useBreakpoint } from 'react-basics';
|
||||
import { useFormat, useLocale, useMessages } from 'components/hooks';
|
||||
import Profile from 'components/common/Profile';
|
||||
import Avatar from 'components/common/Avatar';
|
||||
import styles from './SessionsTable.module.css';
|
||||
import { formatDate } from 'lib/date';
|
||||
|
||||
@ -16,7 +16,7 @@ export function SessionsTable({ data = [] }: { data: any[]; showDomain?: boolean
|
||||
<GridColumn name="id" label="ID" width="300px">
|
||||
{row => (
|
||||
<Link href={`sessions/${row.id}`} className={styles.link}>
|
||||
<Profile key={row.id} seed={row.id} size={64} />
|
||||
<Avatar key={row.id} seed={row.id} size={64} />
|
||||
{row.id}
|
||||
</Link>
|
||||
)}
|
||||
|
@ -3,7 +3,7 @@ import WebsiteHeader from '../../WebsiteHeader';
|
||||
import SessionInfo from './SessionInfo';
|
||||
import { useWebsiteSession } from 'components/hooks';
|
||||
import { Loading } from 'react-basics';
|
||||
import Profile from 'components/common/Profile';
|
||||
import Avatar from 'components/common/Avatar';
|
||||
import { SessionActivity } from './SessionActivity';
|
||||
import { SessionStats } from './SessionStats';
|
||||
import { SessionData } from './SessionData';
|
||||
@ -27,7 +27,7 @@ export default function SessionDetailsPage({
|
||||
<WebsiteHeader websiteId={websiteId} />
|
||||
<div className={styles.page}>
|
||||
<div className={styles.sidebar}>
|
||||
<Profile seed={data?.id} />
|
||||
<Avatar seed={data?.id} />
|
||||
<SessionInfo data={data} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
|
@ -1,71 +1,23 @@
|
||||
import md5 from 'md5';
|
||||
import { colord, extend } from 'colord';
|
||||
import harmoniesPlugin from 'colord/plugins/harmonies';
|
||||
import mixPlugin from 'colord/plugins/mix';
|
||||
import { useMemo } from 'react';
|
||||
import { createAvatar } from '@dicebear/core';
|
||||
import { lorelei } from '@dicebear/collection';
|
||||
import { getColor, getPastel } from 'lib/colors';
|
||||
|
||||
extend([harmoniesPlugin, mixPlugin]);
|
||||
const lib = lorelei;
|
||||
|
||||
const harmonies = [
|
||||
//'analogous',
|
||||
//'complementary',
|
||||
'double-split-complementary',
|
||||
//'rectangle',
|
||||
'split-complementary',
|
||||
'tetradic',
|
||||
//'triadic',
|
||||
];
|
||||
function Avatar({ seed, size = 128, ...props }: { seed: string; size?: number }) {
|
||||
const backgroundColor = getPastel(getColor(seed), 4);
|
||||
|
||||
const color = (value: string, invert: boolean = false) => {
|
||||
const c = colord(value.startsWith('#') ? value : `#${value}`);
|
||||
const avatar = useMemo(() => {
|
||||
return createAvatar(lib, {
|
||||
...props,
|
||||
seed,
|
||||
size,
|
||||
backgroundColor: [backgroundColor],
|
||||
}).toDataUri();
|
||||
}, []);
|
||||
|
||||
if (invert && c.isDark()) {
|
||||
return c.invert();
|
||||
}
|
||||
|
||||
return c;
|
||||
};
|
||||
|
||||
const remix = (hash: string) => {
|
||||
const a = hash.substring(0, 6);
|
||||
const b = hash.substring(6, 12);
|
||||
const c = hash.substring(12, 18);
|
||||
const d = hash.substring(18, 24);
|
||||
const e = hash.substring(24, 30);
|
||||
const f = hash.substring(30, 32);
|
||||
|
||||
const base = [b, c, d, e]
|
||||
.reduce((acc, val) => {
|
||||
return acc.mix(color(val), 0.05);
|
||||
}, color(a))
|
||||
.saturate(0.1)
|
||||
.toHex();
|
||||
|
||||
const harmony = pick(parseInt(f, 16), harmonies);
|
||||
|
||||
return color(base, true)
|
||||
.harmonies(harmony)
|
||||
.map(c => c.toHex());
|
||||
};
|
||||
|
||||
const pick = (num: number, arr: any[]) => {
|
||||
return arr[num % arr.length];
|
||||
};
|
||||
|
||||
export function Avatar({ value }: { value: string }) {
|
||||
const hash = md5(value);
|
||||
const colors = remix(hash);
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id={`color-${hash}`} gradientTransform="rotate(90)">
|
||||
<stop offset="0%" stopColor={colors[1]} />
|
||||
<stop offset="100%" stopColor={colors[2]} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="50" cy="50" r="50" fill={`url(#color-${hash})`} />
|
||||
</svg>
|
||||
);
|
||||
return <img src={avatar} alt="Avatar" style={{ borderRadius: '100%' }} />;
|
||||
}
|
||||
|
||||
export default Avatar;
|
||||
|
@ -1,41 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { createAvatar } from '@dicebear/core';
|
||||
import { lorelei } from '@dicebear/collection';
|
||||
import md5 from 'md5';
|
||||
|
||||
const lib = lorelei;
|
||||
|
||||
function convertToPastel(hexColor: string, pastelFactor: number = 0.5) {
|
||||
// Remove the # if present
|
||||
hexColor = hexColor.replace(/^#/, '');
|
||||
|
||||
// Convert hex to RGB
|
||||
let r = parseInt(hexColor.substring(0, 2), 16);
|
||||
let g = parseInt(hexColor.substring(2, 4), 16);
|
||||
let b = parseInt(hexColor.substring(4, 6), 16);
|
||||
|
||||
// Calculate pastel version (mix with white)
|
||||
//const pastelFactor = 0.5; // Adjust this value to control pastel intensity
|
||||
|
||||
r = Math.floor((r + 255 * pastelFactor) / (1 + pastelFactor));
|
||||
g = Math.floor((g + 255 * pastelFactor) / (1 + pastelFactor));
|
||||
b = Math.floor((b + 255 * pastelFactor) / (1 + pastelFactor));
|
||||
|
||||
// Convert back to hex
|
||||
return `#${((1 << 24) | (r << 16) | (g << 8) | b).toString(16).slice(1)}`;
|
||||
}
|
||||
|
||||
function Profile({ seed, size = 128, ...props }: { seed: string; size?: number }) {
|
||||
const avatar = useMemo(() => {
|
||||
return createAvatar(lib, {
|
||||
...props,
|
||||
seed,
|
||||
size,
|
||||
backgroundColor: [convertToPastel(md5(seed).substring(0, 6), 2).replace(/^#/, '')],
|
||||
}).toDataUri();
|
||||
}, []);
|
||||
|
||||
return <img src={avatar} alt="Avatar" style={{ borderRadius: '100%' }} />;
|
||||
}
|
||||
|
||||
export default Profile;
|
45
src/lib/colors.ts
Normal file
45
src/lib/colors.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import md5 from 'md5';
|
||||
|
||||
export const pick = (num: number, arr: any[]) => {
|
||||
return arr[num % arr.length];
|
||||
};
|
||||
|
||||
export function clamp(num: number, min: number, max: number) {
|
||||
return num < min ? min : num > max ? max : num;
|
||||
}
|
||||
|
||||
export function hex2RGB(color: string, min: number = 0, max: number = 255) {
|
||||
const c = color.replace(/^#/, '');
|
||||
const diff = max - min;
|
||||
|
||||
const normalize = (num: number) => {
|
||||
return Math.floor((num / 255) * diff + min);
|
||||
};
|
||||
|
||||
const r = normalize(parseInt(c.substring(0, 2), 16));
|
||||
const g = normalize(parseInt(c.substring(2, 4), 16));
|
||||
const b = normalize(parseInt(c.substring(4, 6), 16));
|
||||
|
||||
return { r, g, b };
|
||||
}
|
||||
|
||||
export function rgb2Hex(r: number, g: number, b: number, prefix = '') {
|
||||
return `${prefix}${r.toString(16)}${g.toString(16)}${b.toString(16)}`;
|
||||
}
|
||||
|
||||
export function getPastel(color: string, factor: number = 0.5, prefix = '') {
|
||||
let { r, g, b } = hex2RGB(color);
|
||||
|
||||
r = Math.floor((r + 255 * factor) / (1 + factor));
|
||||
g = Math.floor((g + 255 * factor) / (1 + factor));
|
||||
b = Math.floor((b + 255 * factor) / (1 + factor));
|
||||
|
||||
return rgb2Hex(r, g, b, prefix);
|
||||
}
|
||||
|
||||
export function getColor(seed: string, min: number = 0, max: number = 255) {
|
||||
const color = md5(seed).substring(0, 6);
|
||||
const { r, g, b } = hex2RGB(color, min, max);
|
||||
|
||||
return rgb2Hex(r, g, b);
|
||||
}
|
Loading…
Reference in New Issue
Block a user