Skip to content

Commit

Permalink
OPIK-21 [UX] Improve UX of the trace component (#149)
Browse files Browse the repository at this point in the history
* OPIK-21 [UX] Improve UX of the trace component

* fix letter spacing handling for safari
  • Loading branch information
andriidudar authored Sep 3, 2024
1 parent a966fd5 commit 0e16890
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import useAppStore from "@/store/AppStore";
import { useDatasetIdFromURL } from "@/hooks/useDatasetIdFromURL";
import { DatasetCompareAddExperimentHeader } from "@/components/pages/DatasetCompareExperimentsPage/DatasetCompareAddExperimentHeader";
import {
CELL_VERTICAL_ALIGNMENT,
COLUMN_TYPE,
ColumnData,
OnChangeFn,
Expand Down Expand Up @@ -164,21 +163,11 @@ const DatasetCompareExperimentsPage: React.FunctionComponent = () => {
const retVal = convertColumnDataToColumn<
ExperimentsCompare,
ExperimentsCompare
>(
DEFAULT_COLUMNS.map((c) => {
return height === ROW_HEIGHT.small
? {
...c,
verticalAlignment: CELL_VERTICAL_ALIGNMENT.start,
}
: c;
}),
{
columnsWidth,
selectedColumns,
columnsOrder,
},
);
>(DEFAULT_COLUMNS, {
columnsWidth,
selectedColumns,
columnsOrder,
});

experimentsIds.forEach((id: string) => {
const size = columnsWidth[id] ?? 400;
Expand All @@ -204,14 +193,7 @@ const DatasetCompareExperimentsPage: React.FunctionComponent = () => {
});

return retVal;
}, [
columnsWidth,
selectedColumns,
columnsOrder,
experimentsIds,
setTraceId,
height,
]);
}, [columnsWidth, selectedColumns, columnsOrder, experimentsIds, setTraceId]);

const { data, isPending } = useCompareExperimentsList(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
height: 100%;

.detailsContainerWithArrow {
width: 65%;
min-width: 250px;
width: var(--details-container-width, 65%);
min-width: 100px;
display: flex;
position: relative;

Expand Down Expand Up @@ -163,6 +163,7 @@
}

.chainSpanOuterContainer {
min-width: 150px;
display: flex;
flex-direction: column;
align-items: start;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from "react-complex-tree";
import { BASE_TRACE_DATA_TYPE, Span, Trace } from "@/types/traces";
import { treeRenderers } from "./treeRenderers";
import { calcDuration } from "@/lib/utils";
import { calcDuration, getTextWidth } from "@/lib/utils";
import { SPANS_COLORS_MAP, TRACE_TYPE_FOR_TREE } from "@/constants/traces";
import { Button } from "@/components/ui/button";
import useDeepMemo from "@/hooks/useDeepMemo";
Expand All @@ -32,6 +32,30 @@ type TraceTreeViewerProps = {
onSelectRow: (id: string) => void;
};

type ItemWidthObject = {
id: string;
name: string;
parentId?: string;
children: ItemWidthObject[];
level?: number;
};

const getSpansWithLevel = (
item: ItemWidthObject,
accumulator: ItemWidthObject[] = [],
level = 0,
) => {
accumulator.push({
...item,
level,
});

if (item.children) {
item.children.forEach((i) => getSpansWithLevel(i, accumulator, level + 1));
}
return accumulator;
};

const TraceTreeViewer: React.FunctionComponent<TraceTreeViewerProps> = ({
trace,
spans,
Expand Down Expand Up @@ -133,7 +157,7 @@ const TraceTreeViewer: React.FunctionComponent<TraceTreeViewerProps> = ({
retVal[directParentKey].children?.push(span.id);
}
return retVal;
}, acc);
});

return retVal;
}, [trace, traceSpans]);
Expand All @@ -148,8 +172,64 @@ const TraceTreeViewer: React.FunctionComponent<TraceTreeViewerProps> = ({
[rowId, expandedTraceSpans],
);

const maxWidth = useMemo(() => {
const map: Record<string, number> = {};
const list: ItemWidthObject[] = traceSpans.map((s) => ({
id: s.id,
name: s.name || "",
parentId: s.parent_span_id,
children: [],
}));
const rootElement: ItemWidthObject = {
id: trace.id,
name: trace.name,
children: [],
level: 1,
};

list.forEach((item, index) => {
map[item.id] = index;
});

list.forEach((item) => {
if (item.parentId) {
list[map[item.parentId]].children.push(item);
} else {
rootElement.children.push(item);
}
});

const items = getSpansWithLevel(rootElement, [], 2);

const widthArray = getTextWidth(
items.map((i) => i.name),
{ font: "14px Inter" },
);

const OTHER_SPACE = 52;
const LEVEL_WIDTH = 16;

return Math.ceil(
Math.max(
...items.map(
(i, index) =>
OTHER_SPACE +
(i.level || 1) * LEVEL_WIDTH +
widthArray[index] * 1.03, //where 1.03 Letter spacing compensation
),
),
);
}, [traceSpans, trace]);

return (
<div className="size-full max-w-full overflow-auto py-4">
<div
className="size-full max-w-full overflow-auto py-4"
style={
{
"--details-container-width": `${maxWidth}px`,
} as React.CSSProperties
}
>
<div className="min-w-[400px] max-w-full overflow-x-hidden">
<div className="flex flex-row items-end gap-2 px-6 py-2">
<div className="comet-title-m">Trace spans</div>
Expand Down
16 changes: 16 additions & 0 deletions apps/opik-frontend/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,19 @@ export const millisecondsToSeconds = (milliseconds: number) => {
// rounds with precision, one character after the point
return round(milliseconds / 1000, 1);
};

export const getTextWidth = (
text: string[],
properties: {
font?: string;
} = {},
) => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d") as CanvasRenderingContext2D;

if (properties.font) {
context.font = properties.font;
}

return text.map((v) => context.measureText(v).width);
};

0 comments on commit 0e16890

Please sign in to comment.