224 lines
5.4 KiB
TypeScript
224 lines
5.4 KiB
TypeScript
import React, {
|
|
FC,
|
|
useRef,
|
|
useEffect,
|
|
useMemo,
|
|
memo,
|
|
} from '../../lib/teact/teact';
|
|
|
|
import buildClassName from '../../util/buildClassName';
|
|
|
|
type OwnProps = {
|
|
count: number;
|
|
index: number;
|
|
};
|
|
|
|
const BORDER_MASK_LEVEL = 4;
|
|
|
|
const PinnedMessageNavigation: FC<OwnProps> = ({
|
|
count, index,
|
|
}) => {
|
|
// eslint-disable-next-line no-null/no-null
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
const markupParams = useMemo(() => {
|
|
return calculateMarkup(count, index);
|
|
}, [count, index]);
|
|
|
|
useEffect(() => {
|
|
if (!containerRef.current) {
|
|
return;
|
|
}
|
|
|
|
const {
|
|
trackHeight,
|
|
trackTranslateY,
|
|
markHeight,
|
|
markTranslateY,
|
|
clipPathId,
|
|
clipPath,
|
|
} = markupParams;
|
|
|
|
const firstChild = containerRef.current.firstElementChild;
|
|
if (containerRef && containerRef.current) {
|
|
const currentElement = containerRef.current;
|
|
const { style } = currentElement;
|
|
style.height = `${trackHeight}px`;
|
|
style.transform = `translateY(-${trackTranslateY}px)`;
|
|
style.clipPath = `url("#${clipPathId}")`;
|
|
const svg = currentElement.querySelector('svg');
|
|
const div = currentElement.querySelector('div');
|
|
const defs = currentElement.querySelector('defs');
|
|
if (!svg) {
|
|
if (firstChild) {
|
|
firstChild.innerHTML = `<svg height="0" width="0"><defs> ${clipPath} </defs></svg>`;
|
|
}
|
|
}
|
|
if (defs) {
|
|
defs.innerHTML = clipPath;
|
|
}
|
|
if (div) {
|
|
div.style.height = `${markHeight}px`;
|
|
div.style.transform = `translateY(${markTranslateY}px)`;
|
|
}
|
|
}
|
|
}, [markupParams]);
|
|
|
|
if (count === 1) {
|
|
return (
|
|
<div className="pinned-message-border">
|
|
<div
|
|
className="pinned-message-border-wrapper-1"
|
|
ref={containerRef}
|
|
/>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const {
|
|
trackHeight, trackTranslateY, markHeight, markTranslateY, clipPathId,
|
|
} = markupParams;
|
|
|
|
return (
|
|
<div className={buildClassName('pinned-message-border', count > BORDER_MASK_LEVEL && 'pinned-message-border-mask')}>
|
|
<div
|
|
className="pinned-message-border-wrapper"
|
|
ref={containerRef}
|
|
// @ts-ignore
|
|
style={
|
|
`clip-path: url("#${clipPathId}"); width: 2px;
|
|
height: ${trackHeight}px; transform: translateY(-${trackTranslateY}px);`
|
|
}
|
|
>
|
|
<span />
|
|
<div
|
|
className="pinned-message-border-mark"
|
|
// @ts-ignore
|
|
style={`height: ${markHeight}px; transform: translateY(${markTranslateY}px);`}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function calculateMarkup(count: number, index: number) {
|
|
const reverseIndex = count - index - 1;
|
|
const barHeight = getBarHeight(count);
|
|
const markHeight = getMarkHeight(count, reverseIndex);
|
|
const trackHeight = getTrackHeight(count, barHeight);
|
|
|
|
const clipPathId = `clipPath${count}`;
|
|
const clipPath = getClipPath(clipPathId, barHeight, count);
|
|
|
|
const markTranslateY = getMarkTranslateY(reverseIndex, barHeight, count);
|
|
const trackTranslateY = getTrackTranslateY(reverseIndex, count, barHeight, trackHeight);
|
|
return {
|
|
markHeight,
|
|
clipPath,
|
|
markTranslateY,
|
|
trackTranslateY,
|
|
trackHeight,
|
|
clipPathId,
|
|
};
|
|
}
|
|
|
|
function getBarHeight(count: number): number {
|
|
let barHeight = 8;
|
|
if (count === 1) {
|
|
barHeight = 36;
|
|
} else if (count === 2) {
|
|
barHeight = 17;
|
|
} else if (count === 3) {
|
|
barHeight = 11;
|
|
} else if (count === 4) {
|
|
barHeight = 7.5;
|
|
} else if (count > 3) {
|
|
barHeight = 7.5;
|
|
}
|
|
|
|
return barHeight;
|
|
}
|
|
|
|
function getMarkHeight(count: number, index: number) {
|
|
let barHeight = 36;
|
|
if (count === 1) {
|
|
barHeight = 36;
|
|
} else if (count === 2) {
|
|
barHeight = 17;
|
|
} else if (count === 3) {
|
|
barHeight = index === 1 ? 12 : 11;
|
|
} else if (count === 4) {
|
|
barHeight = 7.5;
|
|
} else if (count > 3) {
|
|
barHeight = 7.5;
|
|
}
|
|
|
|
return barHeight;
|
|
}
|
|
|
|
function getTrackHeight(count: number, barHeight: number) {
|
|
return count <= 3 ? 36 : barHeight * count + 2 * (count - 1);
|
|
}
|
|
|
|
function getClipPath(id: string, barHeight: number, count: number) {
|
|
const radius = 1;
|
|
|
|
let d = '';
|
|
if (count === 3) {
|
|
d = drawRect(0, 0, 2, barHeight, radius)
|
|
+ drawRect(0, 12, 2, barHeight + 1, radius)
|
|
+ drawRect(0, 25, 2, barHeight, radius);
|
|
} else {
|
|
for (let i = 0; i < count; i++) {
|
|
d += drawRect(0, (barHeight + 2) * i, 2, barHeight, radius);
|
|
}
|
|
}
|
|
|
|
return (
|
|
`<clipPath id="${id}">
|
|
<path d="${d}" />
|
|
</clipPath>`
|
|
);
|
|
}
|
|
|
|
function drawRect(x: number, y: number, width: number, height: number, radius: number) {
|
|
return `M${x},${y + radius}a${radius},${radius},0,0,1,
|
|
${width},0v${height - 2 * radius}a${radius},${radius},0,0,1,${-width},0Z`;
|
|
}
|
|
|
|
function getMarkTranslateY(index: number, barHeight: number, count: number) {
|
|
if (count === 1) {
|
|
return 0;
|
|
} else if (count === 2) {
|
|
return index === 0 ? 0 : barHeight + 2;
|
|
}
|
|
|
|
if (count === 3) {
|
|
if (index === 0) {
|
|
return 0;
|
|
} else if (index === 1) {
|
|
return 12;
|
|
}
|
|
|
|
return 25;
|
|
} else {
|
|
return (barHeight + 2) * index;
|
|
}
|
|
}
|
|
|
|
function getTrackTranslateY(index: number, count: number, barHeight: number, trackHeight: number) {
|
|
if (count <= 4) {
|
|
return 0;
|
|
}
|
|
|
|
if (index <= 1) {
|
|
return 0;
|
|
} else if (index >= count - 2) {
|
|
return trackHeight - 36;
|
|
}
|
|
|
|
return (barHeight + 4) / 2 + (index - 2) * (barHeight + 2);
|
|
}
|
|
|
|
export default memo(PinnedMessageNavigation);
|