Skip to content

Commit 8a215f7

Browse files
committed
feat(Storybook): add custom marker story
1 parent 763f687 commit 8a215f7

File tree

2 files changed

+128
-2
lines changed

2 files changed

+128
-2
lines changed

src/stories/Markers.stories.tsx

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,27 @@ import { baseTimelineConfig } from "./configs/events";
44
import {
55
collapsedBaseConfig,
66
markersBaseConfig,
7+
markersCustomRenderer,
78
markersWithLabelsConfig,
89
} from "./configs/markers";
910
import { defaultViewConfig } from "../constants/options";
1011
import { StoryWrapper } from "./StoryWrapper";
11-
import { TimelineEvent, TimelineSettings, ViewConfiguration } from "../types";
12+
import {
13+
TimelineEvent,
14+
TimelineMarker,
15+
TimelineSettings,
16+
ViewConfiguration,
17+
} from "../types";
1218

1319
type ViewConfigurationControls = {
1420
[K in keyof ViewConfiguration as `viewConfiguration.${K}`]: ViewConfiguration[K];
1521
};
1622

1723
type SettingsControls = {
18-
[K in keyof TimelineSettings<TimelineEvent> as `settings.${K}`]: TimelineSettings<TimelineEvent>[K];
24+
[K in keyof TimelineSettings<
25+
TimelineEvent,
26+
TimelineMarker
27+
> as `settings.${K}`]: TimelineSettings<TimelineEvent, TimelineMarker>[K];
1928
};
2029

2130
type StoryProps = SettingsControls & ViewConfigurationControls;
@@ -234,3 +243,23 @@ export const WithLabels: Story = {
234243
},
235244
},
236245
};
246+
247+
export const CustomRenderer: Story = {
248+
args: {
249+
"settings.start": markersCustomRenderer.settings.start,
250+
"settings.end": markersCustomRenderer.settings.end,
251+
"settings.axes": markersCustomRenderer.settings.axes,
252+
"settings.events": markersCustomRenderer.settings.events,
253+
"settings.markers": markersCustomRenderer.settings.markers,
254+
"settings.selectedEventIds": baseTimelineConfig.settings.selectedEventIds,
255+
...defaultViewConfigArgs,
256+
},
257+
parameters: {
258+
storyKey: "custom renderer",
259+
docs: {
260+
description: {
261+
story: "Demonstration of a timeline with custom renderer",
262+
},
263+
},
264+
},
265+
};

src/stories/MyMarkerRenderer.tsx

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { AbstractMarkerRenderer } from "../components/Markers/AbstractMarkerRenderer";
2+
import { LabelSize, TimelineMarker, ViewConfiguration } from "../types";
3+
import { clamp } from "../helpers/math";
4+
5+
export type MyMarker = TimelineMarker & {
6+
activeColor: string;
7+
hoverColor: string;
8+
lineWidth: number;
9+
};
10+
11+
const DEFAULT_LABEL_PADDING = 4;
12+
13+
export class MyMarkerRenderer<
14+
TMarker extends TimelineMarker,
15+
> extends AbstractMarkerRenderer<TMarker> {
16+
public render({
17+
ctx,
18+
isSelected,
19+
isHovered,
20+
marker,
21+
markerPosition,
22+
viewConfiguration,
23+
lastRenderedLabelPosition,
24+
getLabelSize,
25+
}: {
26+
ctx: CanvasRenderingContext2D;
27+
marker: TMarker;
28+
isSelected: boolean;
29+
isHovered: boolean;
30+
markerPosition: number;
31+
viewConfiguration: ViewConfiguration;
32+
lastRenderedLabelPosition: { top: number; bottom: number };
33+
getLabelSize: (label: string) => LabelSize;
34+
}) {
35+
let color = isHovered ? marker.hoverColor : marker.color;
36+
if (isSelected) {
37+
color = marker.activeColor;
38+
}
39+
40+
// Draw marker line
41+
ctx.strokeStyle = color;
42+
ctx.lineWidth = marker.lineWidth;
43+
ctx.setLineDash([5, 3]);
44+
ctx.beginPath();
45+
ctx.moveTo(markerPosition, 0);
46+
ctx.lineTo(markerPosition, ctx.canvas.height);
47+
ctx.stroke();
48+
49+
if (!marker.label) return;
50+
51+
if (isHovered || isSelected) {
52+
this.renderLabel(
53+
ctx,
54+
color,
55+
marker,
56+
markerPosition,
57+
viewConfiguration.markers,
58+
lastRenderedLabelPosition,
59+
getLabelSize,
60+
);
61+
}
62+
}
63+
64+
protected renderLabel(
65+
ctx: CanvasRenderingContext2D,
66+
color: string,
67+
marker: TimelineMarker,
68+
markerPosition: number,
69+
markerConfiguration: ViewConfiguration["markers"],
70+
lastRenderedLabelPosition: { top: number; bottom: number },
71+
getLabelSize: (label: string) => LabelSize,
72+
) {
73+
const { width, height } = getLabelSize(marker.label);
74+
const widthWithPadding = width + DEFAULT_LABEL_PADDING * 2;
75+
const heightWithPadding = height + DEFAULT_LABEL_PADDING * 2;
76+
//
77+
const labelPosition = clamp(
78+
markerPosition - widthWithPadding / 2, // Center label on marker
79+
0, // Don't go past the left edge
80+
Math.min(ctx.canvas.width, lastRenderedLabelPosition["top"]) -
81+
widthWithPadding, // Don't overlap previous labels
82+
);
83+
84+
ctx.font = markerConfiguration.font;
85+
ctx.fillStyle = color;
86+
ctx.roundRect(labelPosition, 0, widthWithPadding, heightWithPadding, 4);
87+
ctx.fill();
88+
89+
//Draw label text
90+
ctx.fillStyle = marker.labelColor;
91+
ctx.fillText(
92+
marker.label,
93+
labelPosition + DEFAULT_LABEL_PADDING,
94+
height + DEFAULT_LABEL_PADDING,
95+
);
96+
}
97+
}

0 commit comments

Comments
 (0)