chore: phrasing
This commit is contained in:
@@ -1,8 +1,13 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from "react";
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
import { fetchDailyCounts, fetchLanguageDailyCounts, fetchProjects, fetchSources } from '../api/client';
|
import {
|
||||||
|
fetchDailyCounts,
|
||||||
|
fetchLanguageDailyCounts,
|
||||||
|
fetchProjects,
|
||||||
|
fetchSources,
|
||||||
|
} from "../api/client";
|
||||||
|
|
||||||
const CELL_SIZE = 12;
|
const CELL_SIZE = 12;
|
||||||
const GAP = 3;
|
const GAP = 3;
|
||||||
@@ -11,11 +16,24 @@ const ROWS = 7;
|
|||||||
const LEFT_LABEL_WIDTH = 28;
|
const LEFT_LABEL_WIDTH = 28;
|
||||||
const TOP_LABEL_HEIGHT = 16;
|
const TOP_LABEL_HEIGHT = 16;
|
||||||
|
|
||||||
const DAY_LABELS = ['', 'mon', '', 'wed', '', 'fri', ''];
|
const DAY_LABELS = ["", "mon", "", "wed", "", "fri", ""];
|
||||||
const MONTH_LABELS = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'];
|
const MONTH_LABELS = [
|
||||||
|
"jan",
|
||||||
|
"feb",
|
||||||
|
"mar",
|
||||||
|
"apr",
|
||||||
|
"may",
|
||||||
|
"jun",
|
||||||
|
"jul",
|
||||||
|
"aug",
|
||||||
|
"sep",
|
||||||
|
"oct",
|
||||||
|
"nov",
|
||||||
|
"dec",
|
||||||
|
];
|
||||||
|
|
||||||
const EMPTY_COLOR = 'rgba(255,255,255,0.05)';
|
const EMPTY_COLOR = "rgba(255,255,255,0.05)";
|
||||||
const FALLBACK_COLOR = '#39d353';
|
const FALLBACK_COLOR = "#39d353";
|
||||||
|
|
||||||
/** Daily contribution graph — last 1 year, one circle per day. */
|
/** Daily contribution graph — last 1 year, one circle per day. */
|
||||||
export function ContributionGraph() {
|
export function ContributionGraph() {
|
||||||
@@ -27,19 +45,19 @@ export function ContributionGraph() {
|
|||||||
const toStr = fmt(to);
|
const toStr = fmt(to);
|
||||||
|
|
||||||
const dailyQ = useQuery({
|
const dailyQ = useQuery({
|
||||||
queryKey: ['daily-counts', fromStr, toStr],
|
queryKey: ["daily-counts", fromStr, toStr],
|
||||||
queryFn: () => fetchDailyCounts(fromStr, toStr),
|
queryFn: () => fetchDailyCounts(fromStr, toStr),
|
||||||
staleTime: 5 * 60_000,
|
staleTime: 5 * 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const langQ = useQuery({
|
const langQ = useQuery({
|
||||||
queryKey: ['language-daily', fromStr, toStr],
|
queryKey: ["language-daily", fromStr, toStr],
|
||||||
queryFn: () => fetchLanguageDailyCounts(fromStr, toStr),
|
queryFn: () => fetchLanguageDailyCounts(fromStr, toStr),
|
||||||
staleTime: 5 * 60_000,
|
staleTime: 5 * 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const projectsQ = useQuery({
|
const projectsQ = useQuery({
|
||||||
queryKey: ['projects'],
|
queryKey: ["projects"],
|
||||||
queryFn: fetchProjects,
|
queryFn: fetchProjects,
|
||||||
staleTime: 60_000,
|
staleTime: 60_000,
|
||||||
});
|
});
|
||||||
@@ -49,7 +67,9 @@ export function ContributionGraph() {
|
|||||||
const fromMs = from.getTime();
|
const fromMs = from.getTime();
|
||||||
const toMs = to.getTime();
|
const toMs = to.getTime();
|
||||||
return projectsQ.data.filter((p) => {
|
return projectsQ.data.filter((p) => {
|
||||||
const first = p.first_activity ? new Date(p.first_activity).getTime() : Infinity;
|
const first = p.first_activity
|
||||||
|
? new Date(p.first_activity).getTime()
|
||||||
|
: Infinity;
|
||||||
const last = p.last_activity ? new Date(p.last_activity).getTime() : 0;
|
const last = p.last_activity ? new Date(p.last_activity).getTime() : 0;
|
||||||
return last >= fromMs && first <= toMs;
|
return last >= fromMs && first <= toMs;
|
||||||
}).length;
|
}).length;
|
||||||
@@ -69,14 +89,15 @@ export function ContributionGraph() {
|
|||||||
const start = new Date(from);
|
const start = new Date(from);
|
||||||
start.setDate(start.getDate() - start.getDay());
|
start.setDate(start.getDate() - start.getDay());
|
||||||
|
|
||||||
const weeks: { date: string; count: number; col: number; row: number }[][] = [];
|
const weeks: { date: string; count: number; col: number; row: number }[][] =
|
||||||
|
[];
|
||||||
const monthMarkers: { col: number; label: string }[] = [];
|
const monthMarkers: { col: number; label: string }[] = [];
|
||||||
let col = 0;
|
let col = 0;
|
||||||
let prevMonth = -1;
|
let prevMonth = -1;
|
||||||
const cursor = new Date(start);
|
const cursor = new Date(start);
|
||||||
|
|
||||||
while (cursor <= to) {
|
while (cursor <= to) {
|
||||||
const week: typeof weeks[0] = [];
|
const week: (typeof weeks)[0] = [];
|
||||||
for (let row = 0; row < ROWS; row++) {
|
for (let row = 0; row < ROWS; row++) {
|
||||||
const dateStr = fmt(cursor);
|
const dateStr = fmt(cursor);
|
||||||
const count = countMap.get(dateStr) ?? 0;
|
const count = countMap.get(dateStr) ?? 0;
|
||||||
@@ -94,7 +115,10 @@ export function ContributionGraph() {
|
|||||||
col++;
|
col++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonZero = counts.map((d) => d.count).filter((c) => c > 0).sort((a, b) => a - b);
|
const nonZero = counts
|
||||||
|
.map((d) => d.count)
|
||||||
|
.filter((c) => c > 0)
|
||||||
|
.sort((a, b) => a - b);
|
||||||
const thresholds = computeThresholds(nonZero);
|
const thresholds = computeThresholds(nonZero);
|
||||||
const totalCount = counts.reduce((sum, d) => sum + d.count, 0);
|
const totalCount = counts.reduce((sum, d) => sum + d.count, 0);
|
||||||
|
|
||||||
@@ -105,17 +129,23 @@ export function ContributionGraph() {
|
|||||||
const svgWidth = LEFT_LABEL_WIDTH + cols * (CELL_SIZE + GAP);
|
const svgWidth = LEFT_LABEL_WIDTH + cols * (CELL_SIZE + GAP);
|
||||||
const svgHeight = TOP_LABEL_HEIGHT + ROWS * (CELL_SIZE + GAP);
|
const svgHeight = TOP_LABEL_HEIGHT + ROWS * (CELL_SIZE + GAP);
|
||||||
|
|
||||||
if (dailyQ.isLoading) return <p style={{ fontSize: '0.8rem' }}>loading contribution graph...</p>;
|
if (dailyQ.isLoading)
|
||||||
|
return <p style={{ fontSize: "0.8rem" }}>loading contribution graph...</p>;
|
||||||
if (dailyQ.isError) return null;
|
if (dailyQ.isError) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="contribution-graph mb-3">
|
<div className="contribution-graph mb-3">
|
||||||
<p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
|
<p style={{ fontSize: "0.8rem", opacity: 0.6 }}>
|
||||||
{totalCount} contributions in the last year
|
{new Intl.NumberFormat().format(totalCount)} contributions
|
||||||
{repoCount > 0 && ` in ${repoCount} repositories`}
|
{repoCount > 0 && `, across ${repoCount} repositories, `}
|
||||||
|
in the last year
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`} width="100%" className="d-block">
|
<svg
|
||||||
|
viewBox={`0 0 ${svgWidth} ${svgHeight}`}
|
||||||
|
width="100%"
|
||||||
|
className="d-block"
|
||||||
|
>
|
||||||
{DAY_LABELS.map((label, i) =>
|
{DAY_LABELS.map((label, i) =>
|
||||||
label ? (
|
label ? (
|
||||||
<text
|
<text
|
||||||
@@ -148,12 +178,16 @@ export function ContributionGraph() {
|
|||||||
cx={LEFT_LABEL_WIDTH + col * (CELL_SIZE + GAP) + RADIUS}
|
cx={LEFT_LABEL_WIDTH + col * (CELL_SIZE + GAP) + RADIUS}
|
||||||
cy={TOP_LABEL_HEIGHT + row * (CELL_SIZE + GAP) + RADIUS}
|
cy={TOP_LABEL_HEIGHT + row * (CELL_SIZE + GAP) + RADIUS}
|
||||||
r={RADIUS - 1}
|
r={RADIUS - 1}
|
||||||
fill={count === 0 ? EMPTY_COLOR : (dayColorMap.get(date) ?? FALLBACK_COLOR)}
|
fill={
|
||||||
|
count === 0
|
||||||
|
? EMPTY_COLOR
|
||||||
|
: (dayColorMap.get(date) ?? FALLBACK_COLOR)
|
||||||
|
}
|
||||||
opacity={count === 0 ? 1 : opacityFor(count, thresholds)}
|
opacity={count === 0 ? 1 : opacityFor(count, thresholds)}
|
||||||
className="graph-cell"
|
className="graph-cell"
|
||||||
onClick={() => navigate(`/activity/${date}`)}
|
onClick={() => navigate(`/activity/${date}`)}
|
||||||
>
|
>
|
||||||
<title>{`${date}: ${count} ${count === 1 ? 'contribution' : 'contributions'}`}</title>
|
<title>{`${date}: ${count} ${count === 1 ? "contribution" : "contributions"}`}</title>
|
||||||
</circle>
|
</circle>
|
||||||
)),
|
)),
|
||||||
)}
|
)}
|
||||||
@@ -166,7 +200,7 @@ export function ContributionGraph() {
|
|||||||
/** All-time monthly contribution graph — years on X axis, months on Y axis. */
|
/** All-time monthly contribution graph — years on X axis, months on Y axis. */
|
||||||
export function AllTimeGraph() {
|
export function AllTimeGraph() {
|
||||||
const sourcesQ = useQuery({
|
const sourcesQ = useQuery({
|
||||||
queryKey: ['sources'],
|
queryKey: ["sources"],
|
||||||
queryFn: fetchSources,
|
queryFn: fetchSources,
|
||||||
staleTime: 60_000,
|
staleTime: 60_000,
|
||||||
});
|
});
|
||||||
@@ -177,11 +211,13 @@ export function AllTimeGraph() {
|
|||||||
.map((s) => s.earliest)
|
.map((s) => s.earliest)
|
||||||
.filter((d): d is string => d != null)
|
.filter((d): d is string => d != null)
|
||||||
.map((d) => new Date(d));
|
.map((d) => new Date(d));
|
||||||
return dates.length > 0 ? new Date(Math.min(...dates.map((d) => d.getTime()))) : null;
|
return dates.length > 0
|
||||||
|
? new Date(Math.min(...dates.map((d) => d.getTime())))
|
||||||
|
: null;
|
||||||
}, [sourcesQ.data]);
|
}, [sourcesQ.data]);
|
||||||
|
|
||||||
const projectsQ = useQuery({
|
const projectsQ = useQuery({
|
||||||
queryKey: ['projects'],
|
queryKey: ["projects"],
|
||||||
queryFn: fetchProjects,
|
queryFn: fetchProjects,
|
||||||
staleTime: 60_000,
|
staleTime: 60_000,
|
||||||
});
|
});
|
||||||
@@ -193,14 +229,14 @@ export function AllTimeGraph() {
|
|||||||
const toStr = fmt(to);
|
const toStr = fmt(to);
|
||||||
|
|
||||||
const dailyQ = useQuery({
|
const dailyQ = useQuery({
|
||||||
queryKey: ['daily-counts-alltime', fromStr, toStr],
|
queryKey: ["daily-counts-alltime", fromStr, toStr],
|
||||||
queryFn: () => fetchDailyCounts(fromStr, toStr),
|
queryFn: () => fetchDailyCounts(fromStr, toStr),
|
||||||
enabled: !!earliest,
|
enabled: !!earliest,
|
||||||
staleTime: 10 * 60_000,
|
staleTime: 10 * 60_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const langQ = useQuery({
|
const langQ = useQuery({
|
||||||
queryKey: ['language-daily-alltime', fromStr, toStr],
|
queryKey: ["language-daily-alltime", fromStr, toStr],
|
||||||
queryFn: () => fetchLanguageDailyCounts(fromStr, toStr),
|
queryFn: () => fetchLanguageDailyCounts(fromStr, toStr),
|
||||||
enabled: !!earliest,
|
enabled: !!earliest,
|
||||||
staleTime: 10 * 60_000,
|
staleTime: 10 * 60_000,
|
||||||
@@ -212,7 +248,10 @@ export function AllTimeGraph() {
|
|||||||
const monthColorMap = useMemo(() => {
|
const monthColorMap = useMemo(() => {
|
||||||
const entries = langQ.data ?? [];
|
const entries = langQ.data ?? [];
|
||||||
if (entries.length === 0) return new Map<string, string>();
|
if (entries.length === 0) return new Map<string, string>();
|
||||||
const map = new Map<string, Map<string, { commits: number; color: string }>>();
|
const map = new Map<
|
||||||
|
string,
|
||||||
|
Map<string, { commits: number; color: string }>
|
||||||
|
>();
|
||||||
for (const e of entries) {
|
for (const e of entries) {
|
||||||
const key = e.date.slice(0, 7); // YYYY-MM
|
const key = e.date.slice(0, 7); // YYYY-MM
|
||||||
if (!map.has(key)) map.set(key, new Map());
|
if (!map.has(key)) map.set(key, new Map());
|
||||||
@@ -221,7 +260,10 @@ export function AllTimeGraph() {
|
|||||||
if (cur) {
|
if (cur) {
|
||||||
cur.commits += e.commits;
|
cur.commits += e.commits;
|
||||||
} else {
|
} else {
|
||||||
langMap.set(e.language, { commits: e.commits, color: e.color ?? FALLBACK_COLOR });
|
langMap.set(e.language, {
|
||||||
|
commits: e.commits,
|
||||||
|
color: e.color ?? FALLBACK_COLOR,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const result = new Map<string, string>();
|
const result = new Map<string, string>();
|
||||||
@@ -237,7 +279,8 @@ export function AllTimeGraph() {
|
|||||||
|
|
||||||
const { years, monthGrid, thresholds, totalCount } = useMemo(() => {
|
const { years, monthGrid, thresholds, totalCount } = useMemo(() => {
|
||||||
const counts = dailyQ.data ?? [];
|
const counts = dailyQ.data ?? [];
|
||||||
if (counts.length === 0) return { years: [], monthGrid: [], thresholds: [1, 2, 3], totalCount: 0 };
|
if (counts.length === 0)
|
||||||
|
return { years: [], monthGrid: [], thresholds: [1, 2, 3], totalCount: 0 };
|
||||||
|
|
||||||
const countMap = new Map(counts.map((d) => [d.date, d.count]));
|
const countMap = new Map(counts.map((d) => [d.date, d.count]));
|
||||||
|
|
||||||
@@ -247,16 +290,30 @@ export function AllTimeGraph() {
|
|||||||
for (let yr = startYear; yr <= endYear; yr++) years.push(yr);
|
for (let yr = startYear; yr <= endYear; yr++) years.push(yr);
|
||||||
|
|
||||||
// Build a 12 x years grid of monthly totals
|
// Build a 12 x years grid of monthly totals
|
||||||
const monthGrid: { year: number; month: number; count: number; monthStart: string; monthEnd: string; monthKey: string }[][] = [];
|
const monthGrid: {
|
||||||
|
year: number;
|
||||||
|
month: number;
|
||||||
|
count: number;
|
||||||
|
monthStart: string;
|
||||||
|
monthEnd: string;
|
||||||
|
monthKey: string;
|
||||||
|
}[][] = [];
|
||||||
for (let m = 0; m < 12; m++) {
|
for (let m = 0; m < 12; m++) {
|
||||||
const row: typeof monthGrid[0] = [];
|
const row: (typeof monthGrid)[0] = [];
|
||||||
for (const yr of years) {
|
for (const yr of years) {
|
||||||
const monthStart = new Date(yr, m, 1);
|
const monthStart = new Date(yr, m, 1);
|
||||||
const monthEnd = new Date(yr, m + 1, 0); // last day of month
|
const monthEnd = new Date(yr, m + 1, 0); // last day of month
|
||||||
const monthKey = `${yr}-${String(m + 1).padStart(2, '0')}`;
|
const monthKey = `${yr}-${String(m + 1).padStart(2, "0")}`;
|
||||||
// Don't include months entirely outside our data range
|
// Don't include months entirely outside our data range
|
||||||
if (monthStart > to || monthEnd < from) {
|
if (monthStart > to || monthEnd < from) {
|
||||||
row.push({ year: yr, month: m, count: 0, monthStart: fmt(monthStart), monthEnd: fmt(monthEnd), monthKey });
|
row.push({
|
||||||
|
year: yr,
|
||||||
|
month: m,
|
||||||
|
count: 0,
|
||||||
|
monthStart: fmt(monthStart),
|
||||||
|
monthEnd: fmt(monthEnd),
|
||||||
|
monthKey,
|
||||||
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let total = 0;
|
let total = 0;
|
||||||
@@ -265,7 +322,14 @@ export function AllTimeGraph() {
|
|||||||
total += countMap.get(fmt(cursor)) ?? 0;
|
total += countMap.get(fmt(cursor)) ?? 0;
|
||||||
cursor.setDate(cursor.getDate() + 1);
|
cursor.setDate(cursor.getDate() + 1);
|
||||||
}
|
}
|
||||||
row.push({ year: yr, month: m, count: total, monthStart: fmt(monthStart), monthEnd: fmt(monthEnd), monthKey });
|
row.push({
|
||||||
|
year: yr,
|
||||||
|
month: m,
|
||||||
|
count: total,
|
||||||
|
monthStart: fmt(monthStart),
|
||||||
|
monthEnd: fmt(monthEnd),
|
||||||
|
monthKey,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
monthGrid.push(row);
|
monthGrid.push(row);
|
||||||
}
|
}
|
||||||
@@ -290,12 +354,17 @@ export function AllTimeGraph() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="contribution-graph mb-4">
|
<div className="contribution-graph mb-4">
|
||||||
<p style={{ fontSize: '0.8rem', opacity: 0.6 }}>
|
<p style={{ fontSize: "0.8rem", opacity: 0.6 }}>
|
||||||
{totalCount} contributions since {fmt(from)}
|
{new Intl.NumberFormat().format(totalCount)} contributions
|
||||||
{repoCount > 0 && ` in ${repoCount} repositories`}
|
{repoCount > 0 && `, across ${repoCount} repos, `}
|
||||||
|
since {fmt(from).split("-")[0]}
|
||||||
</p>
|
</p>
|
||||||
<div>
|
<div>
|
||||||
<svg viewBox={`0 0 ${svgWidth} ${svgHeight}`} width="100%" className="d-block">
|
<svg
|
||||||
|
viewBox={`0 0 ${svgWidth} ${svgHeight}`}
|
||||||
|
width="100%"
|
||||||
|
className="d-block"
|
||||||
|
>
|
||||||
{/* Year labels along the top */}
|
{/* Year labels along the top */}
|
||||||
{years.map((year, colIdx) => (
|
{years.map((year, colIdx) => (
|
||||||
<text
|
<text
|
||||||
@@ -323,20 +392,28 @@ export function AllTimeGraph() {
|
|||||||
))}
|
))}
|
||||||
{/* Monthly contribution circles */}
|
{/* Monthly contribution circles */}
|
||||||
{monthGrid.map((row, rowIdx) =>
|
{monthGrid.map((row, rowIdx) =>
|
||||||
row.map(({ year, count, monthStart, monthEnd, monthKey }, colIdx) => (
|
row.map(
|
||||||
<circle
|
({ year, count, monthStart, monthEnd, monthKey }, colIdx) => (
|
||||||
key={`${year}-${rowIdx}`}
|
<circle
|
||||||
cx={monthLabelWidth + colIdx * (CELL_SIZE + GAP) + RADIUS}
|
key={`${year}-${rowIdx}`}
|
||||||
cy={topLabelHeight + rowIdx * (CELL_SIZE + GAP) + RADIUS}
|
cx={monthLabelWidth + colIdx * (CELL_SIZE + GAP) + RADIUS}
|
||||||
r={RADIUS - 1}
|
cy={topLabelHeight + rowIdx * (CELL_SIZE + GAP) + RADIUS}
|
||||||
fill={count === 0 ? EMPTY_COLOR : (monthColorMap.get(monthKey) ?? FALLBACK_COLOR)}
|
r={RADIUS - 1}
|
||||||
opacity={count === 0 ? 1 : opacityFor(count, thresholds)}
|
fill={
|
||||||
className="graph-cell"
|
count === 0
|
||||||
onClick={() => navigate(`/activity/${monthStart}..${monthEnd}`)}
|
? EMPTY_COLOR
|
||||||
>
|
: (monthColorMap.get(monthKey) ?? FALLBACK_COLOR)
|
||||||
<title>{`${MONTH_LABELS[rowIdx]} ${year}: ${count} ${count === 1 ? 'contribution' : 'contributions'}`}</title>
|
}
|
||||||
</circle>
|
opacity={count === 0 ? 1 : opacityFor(count, thresholds)}
|
||||||
)),
|
className="graph-cell"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(`/activity/${monthStart}..${monthEnd}`)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<title>{`${MONTH_LABELS[rowIdx]} ${year}: ${count} ${count === 1 ? "contribution" : "contributions"}`}</title>
|
||||||
|
</circle>
|
||||||
|
),
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
@@ -349,7 +426,14 @@ function fmt(d: Date): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Build a map of date → dominant (highest commit count) language color. */
|
/** Build a map of date → dominant (highest commit count) language color. */
|
||||||
function buildDominantColorMap(entries: { date: string; language: string; color: string | null; commits: number }[]): Map<string, string> {
|
function buildDominantColorMap(
|
||||||
|
entries: {
|
||||||
|
date: string;
|
||||||
|
language: string;
|
||||||
|
color: string | null;
|
||||||
|
commits: number;
|
||||||
|
}[],
|
||||||
|
): Map<string, string> {
|
||||||
const map = new Map<string, { commits: number; color: string }>();
|
const map = new Map<string, { commits: number; color: string }>();
|
||||||
for (const e of entries) {
|
for (const e of entries) {
|
||||||
const cur = map.get(e.date);
|
const cur = map.get(e.date);
|
||||||
@@ -374,6 +458,7 @@ function opacityFor(count: number, thresholds: number[]): number {
|
|||||||
|
|
||||||
function computeThresholds(sorted: number[]): number[] {
|
function computeThresholds(sorted: number[]): number[] {
|
||||||
if (sorted.length === 0) return [1, 2, 3];
|
if (sorted.length === 0) return [1, 2, 3];
|
||||||
const p = (pct: number) => sorted[Math.min(Math.floor(pct * sorted.length), sorted.length - 1)];
|
const p = (pct: number) =>
|
||||||
|
sorted[Math.min(Math.floor(pct * sorted.length), sorted.length - 1)];
|
||||||
return [p(0.25), p(0.5), p(0.75)];
|
return [p(0.25), p(0.5), p(0.75)];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user