Skip to content

Commit 378579e

Browse files
xppcnnwyuc
andauthored
fix(slide-renderer): keep arrowheads visible when toggling thumbnails… (#1045)
* fix(slide-renderer): keep arrowheads visible when toggling thumbnails or fullscreen * fix(renderer): bump version to 0.0.5 --------- Co-authored-by: wyuc <wang-yc24@mails.tsinghua.edu.cn>
1 parent e9d8a33 commit 378579e

5 files changed

Lines changed: 79 additions & 16 deletions

File tree

components/slide-renderer/components/element/LineElement/BaseLineElement.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useMemo, useRef, useState, useEffect } from 'react';
3+
import { useMemo, useRef, useState, useEffect, useId } from 'react';
44
import type { PPTLineElement } from '@openmaic/dsl';
55
import { getLineElementPath } from '@/lib/utils/element';
66
import { useElementShadow } from '../hooks/useElementShadow';
@@ -22,6 +22,7 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
2222
const { shadowStyle } = useElementShadow(elementInfo.shadow);
2323
const pathRef = useRef<SVGPathElement>(null);
2424
const [drawComplete, setDrawComplete] = useState(!animate);
25+
const markerId = `${elementInfo.id}-${useId().replaceAll(':', '')}`;
2526

2627
const svgWidth = useMemo(() => {
2728
const width = Math.abs(elementInfo.start[0] - elementInfo.end[0]);
@@ -104,7 +105,7 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
104105
<defs>
105106
{elementInfo.points[0] && (
106107
<LinePointMarker
107-
id={elementInfo.id}
108+
id={markerId}
108109
position="start"
109110
type={elementInfo.points[0]}
110111
color={elementInfo.color}
@@ -113,7 +114,7 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
113114
)}
114115
{elementInfo.points[1] && (
115116
<LinePointMarker
116-
id={elementInfo.id}
117+
id={markerId}
117118
position="end"
118119
type={elementInfo.points[1]}
119120
color={elementInfo.color}
@@ -130,12 +131,12 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
130131
fill="none"
131132
markerStart={
132133
drawComplete && elementInfo.points[0]
133-
? `url(#${elementInfo.id}-${elementInfo.points[0]}-start)`
134+
? `url(#${markerId}-${elementInfo.points[0]}-start)`
134135
: ''
135136
}
136137
markerEnd={
137138
drawComplete && elementInfo.points[1]
138-
? `url(#${elementInfo.id}-${elementInfo.points[1]}-end)`
139+
? `url(#${markerId}-${elementInfo.points[1]}-end)`
139140
: ''
140141
}
141142
/>

components/slide-renderer/components/element/LineElement/index.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useMemo } from 'react';
3+
import { useId, useMemo } from 'react';
44
import type { PPTLineElement } from '@openmaic/dsl';
55
import { getLineElementPath } from '@/lib/utils/element';
66
import { useElementShadow } from '../hooks/useElementShadow';
@@ -19,6 +19,7 @@ export interface LineElementProps {
1919
*/
2020
export function LineElement({ elementInfo, selectElement }: LineElementProps) {
2121
const { shadowStyle } = useElementShadow(elementInfo.shadow);
22+
const markerId = `${elementInfo.id}-${useId().replaceAll(':', '')}`;
2223

2324
const handleSelectElement = (e: React.MouseEvent | React.TouchEvent) => {
2425
if (elementInfo.lock) return;
@@ -79,7 +80,7 @@ export function LineElement({ elementInfo, selectElement }: LineElementProps) {
7980
<defs>
8081
{elementInfo.points[0] && (
8182
<LinePointMarker
82-
id={elementInfo.id}
83+
id={markerId}
8384
position="start"
8485
type={elementInfo.points[0]}
8586
color={elementInfo.color}
@@ -88,7 +89,7 @@ export function LineElement({ elementInfo, selectElement }: LineElementProps) {
8889
)}
8990
{elementInfo.points[1] && (
9091
<LinePointMarker
91-
id={elementInfo.id}
92+
id={markerId}
9293
position="end"
9394
type={elementInfo.points[1]}
9495
color={elementInfo.color}
@@ -105,10 +106,10 @@ export function LineElement({ elementInfo, selectElement }: LineElementProps) {
105106
strokeDasharray={lineDashArray}
106107
fill="none"
107108
markerStart={
108-
elementInfo.points[0] ? `url(#${elementInfo.id}-${elementInfo.points[0]}-start)` : ''
109+
elementInfo.points[0] ? `url(#${markerId}-${elementInfo.points[0]}-start)` : ''
109110
}
110111
markerEnd={
111-
elementInfo.points[1] ? `url(#${elementInfo.id}-${elementInfo.points[1]}-end)` : ''
112+
elementInfo.points[1] ? `url(#${markerId}-${elementInfo.points[1]}-end)` : ''
112113
}
113114
/>
114115
{/* Invisible wider path for easier clicking */}

packages/@openmaic/renderer/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@openmaic/renderer",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"description": "React component for rendering PPTist-style Slide JSON, extracted from OpenMAIC.",
55
"type": "module",
66
"main": "./dist/index.js",

packages/@openmaic/renderer/src/elements/line/BaseLineElement.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useMemo, useRef, useState, useEffect } from 'react';
3+
import { useMemo, useRef, useState, useEffect, useId } from 'react';
44
import type { PPTLineElement } from '@openmaic/dsl';
55
import { getLineElementPath } from '../../utils/element';
66
import { useElementShadow } from '../shared/useElementShadow';
@@ -17,6 +17,7 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
1717
const { shadowStyle } = useElementShadow(elementInfo.shadow);
1818
const pathRef = useRef<SVGPathElement>(null);
1919
const [drawComplete, setDrawComplete] = useState(!animate);
20+
const markerId = `${elementInfo.id}-${useId().replaceAll(':', '')}`;
2021

2122
const svgWidth = useMemo(() => {
2223
const width = Math.abs(elementInfo.start[0] - elementInfo.end[0]);
@@ -94,7 +95,7 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
9495
<defs>
9596
{elementInfo.points[0] && (
9697
<LinePointMarker
97-
id={elementInfo.id}
98+
id={markerId}
9899
position="start"
99100
type={elementInfo.points[0]}
100101
color={elementInfo.color}
@@ -103,7 +104,7 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
103104
)}
104105
{elementInfo.points[1] && (
105106
<LinePointMarker
106-
id={elementInfo.id}
107+
id={markerId}
107108
position="end"
108109
type={elementInfo.points[1]}
109110
color={elementInfo.color}
@@ -120,12 +121,12 @@ export function BaseLineElement({ elementInfo, animate }: BaseLineElementProps)
120121
fill="none"
121122
markerStart={
122123
drawComplete && elementInfo.points[0]
123-
? `url(#${elementInfo.id}-${elementInfo.points[0]}-start)`
124+
? `url(#${markerId}-${elementInfo.points[0]}-start)`
124125
: ''
125126
}
126127
markerEnd={
127128
drawComplete && elementInfo.points[1]
128-
? `url(#${elementInfo.id}-${elementInfo.points[1]}-end)`
129+
? `url(#${markerId}-${elementInfo.points[1]}-end)`
129130
: ''
130131
}
131132
/>
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// @vitest-environment jsdom
2+
import { render } from '@testing-library/react';
3+
import type { PPTLineElement } from '@openmaic/dsl';
4+
import { describe, expect, it } from 'vitest';
5+
6+
import { BaseLineElement } from '../../src/elements/line/BaseLineElement';
7+
8+
const line = {
9+
id: 'shared-line',
10+
type: 'line',
11+
left: 0,
12+
top: 0,
13+
start: [0, 0],
14+
end: [120, 40],
15+
width: 4,
16+
style: 'solid',
17+
color: '#123456',
18+
points: ['arrow', 'arrow'],
19+
} as PPTLineElement;
20+
21+
function markerReferences(svg: SVGSVGElement): string[] {
22+
const path = svg.querySelector(':scope > path');
23+
return ['marker-start', 'marker-end'].map((attribute) => {
24+
const reference = path?.getAttribute(attribute);
25+
expect(reference).toMatch(/^url\(#.+\)$/);
26+
return reference!.slice(5, -1);
27+
});
28+
}
29+
30+
describe('BaseLineElement marker ids', () => {
31+
it('keeps marker references local to each render instance and stable while scaling', () => {
32+
const { container, rerender } = render(
33+
<div style={{ transform: 'scale(1)' }}>
34+
<BaseLineElement elementInfo={line} animate={false} />
35+
<BaseLineElement elementInfo={line} animate={false} />
36+
</div>,
37+
);
38+
39+
const before = Array.from(container.querySelectorAll('svg')).map((svg) => {
40+
const references = markerReferences(svg);
41+
const ownMarkerIds = Array.from(svg.querySelectorAll('marker'), (marker) => marker.id);
42+
43+
expect(references).toEqual(ownMarkerIds);
44+
return references;
45+
});
46+
47+
expect(new Set(before.flat()).size).toBe(4);
48+
49+
rerender(
50+
<div style={{ transform: 'scale(1.75)' }}>
51+
<BaseLineElement elementInfo={line} animate={false} />
52+
<BaseLineElement elementInfo={line} animate={false} />
53+
</div>,
54+
);
55+
56+
const after = Array.from(container.querySelectorAll('svg')).map(markerReferences);
57+
expect(after).toEqual(before);
58+
expect((container.firstElementChild as HTMLElement).style.transform).toBe('scale(1.75)');
59+
});
60+
});

0 commit comments

Comments
 (0)