mirror of
https://github.com/Dispatcharr/Dispatcharr.git
synced 2026-07-28 20:41:19 +00:00
parent
66b95f2ef8
commit
7b23b0a4df
1 changed files with 126 additions and 108 deletions
|
|
@ -492,7 +492,6 @@ export default function TVChannelGuide({ startDate, endDate }) {
|
|||
guideRef.current.scrollLeft = scrollPosition;
|
||||
}
|
||||
};
|
||||
|
||||
// Renders each program block
|
||||
function renderProgram(program, channelStart) {
|
||||
const programKey = `${program.tvg_id}-${program.start_time}`;
|
||||
|
|
@ -522,18 +521,9 @@ export default function TVChannelGuide({ startDate, endDate }) {
|
|||
const isLive = now.isAfter(programStart) && now.isBefore(programEnd);
|
||||
|
||||
// Determine if the program has ended
|
||||
const isPast = now.isAfter(programEnd);
|
||||
|
||||
// Check if this program is expanded
|
||||
const isPast = now.isAfter(programEnd); // Check if this program is expanded
|
||||
const isExpanded = expandedProgramId === program.id;
|
||||
|
||||
// Calculate how much of the program is cut off
|
||||
const cutOffMinutes = Math.max(
|
||||
0,
|
||||
channelStart.diff(programStart, 'minute')
|
||||
);
|
||||
const cutOffPx = (cutOffMinutes / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH;
|
||||
|
||||
// Set the height based on expanded state
|
||||
const rowHeight = isExpanded ? EXPANDED_PROGRAM_HEIGHT : PROGRAM_HEIGHT;
|
||||
|
||||
|
|
@ -542,6 +532,25 @@ export default function TVChannelGuide({ startDate, endDate }) {
|
|||
const MIN_EXPANDED_WIDTH = 450; // Minimum width in pixels when expanded
|
||||
const expandedWidthPx = Math.max(widthPx, MIN_EXPANDED_WIDTH);
|
||||
|
||||
// Calculate text positioning for long programs that start before the visible area
|
||||
const currentScrollLeft = guideRef.current?.scrollLeft || 0;
|
||||
const programStartInView = leftPx + gapSize;
|
||||
const programEndInView = leftPx + gapSize + widthPx;
|
||||
const viewportLeft = currentScrollLeft;
|
||||
|
||||
// Check if program starts before viewport but extends into it
|
||||
const startsBeforeView = programStartInView < viewportLeft;
|
||||
const extendsIntoView = programEndInView > viewportLeft;
|
||||
|
||||
// Calculate text offset to position it at the visible portion
|
||||
let textOffsetLeft = 0;
|
||||
if (startsBeforeView && extendsIntoView) {
|
||||
// Position text at the start of the visible area, but not beyond the program end
|
||||
const visibleStart = Math.max(viewportLeft - programStartInView, 0);
|
||||
const maxOffset = widthPx - 200; // Leave some space for text, don't push to very end
|
||||
textOffsetLeft = Math.min(visibleStart, maxOffset);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="guide-program-container"
|
||||
|
|
@ -588,7 +597,12 @@ export default function TVChannelGuide({ startDate, endDate }) {
|
|||
transition: 'all 0.2s ease',
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box
|
||||
style={{
|
||||
transform: `translateX(${textOffsetLeft}px)`,
|
||||
transition: 'transform 0.1s ease-out',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
component="div"
|
||||
size={isExpanded ? 'lg' : 'md'}
|
||||
|
|
@ -624,23 +638,28 @@ export default function TVChannelGuide({ startDate, endDate }) {
|
|||
>
|
||||
{programStart.format('h:mma')} - {programEnd.format('h:mma')}
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{/* Description is always shown but expands when row is expanded */}
|
||||
</Box> {/* Description is always shown but expands when row is expanded */}
|
||||
{program.description && (
|
||||
<Text
|
||||
size="xs"
|
||||
<Box
|
||||
style={{
|
||||
marginTop: '4px',
|
||||
whiteSpace: isExpanded ? 'normal' : 'nowrap',
|
||||
textOverflow: isExpanded ? 'clip' : 'ellipsis',
|
||||
overflow: isExpanded ? 'auto' : 'hidden',
|
||||
color: isPast ? '#718096' : '#cbd5e0',
|
||||
maxHeight: isExpanded ? '80px' : 'unset',
|
||||
transform: `translateX(${textOffsetLeft}px)`,
|
||||
transition: 'transform 0.1s ease-out',
|
||||
}}
|
||||
>
|
||||
{program.description}
|
||||
</Text>
|
||||
<Text
|
||||
size="xs"
|
||||
style={{
|
||||
marginTop: '4px',
|
||||
whiteSpace: isExpanded ? 'normal' : 'nowrap',
|
||||
textOverflow: isExpanded ? 'clip' : 'ellipsis',
|
||||
overflow: isExpanded ? 'auto' : 'hidden',
|
||||
color: isPast ? '#718096' : '#cbd5e0',
|
||||
maxHeight: isExpanded ? '80px' : 'unset',
|
||||
}}
|
||||
>
|
||||
{program.description}
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Expanded content */}
|
||||
|
|
@ -906,101 +925,100 @@ export default function TVChannelGuide({ startDate, endDate }) {
|
|||
borderBottom: '1px solid #27272A',
|
||||
width: hourTimeline.length * HOUR_WIDTH,
|
||||
}}
|
||||
>
|
||||
{hourTimeline.map((hourData, hourIndex) => {
|
||||
const { time, isNewDay, dayLabel } = hourData;
|
||||
> {hourTimeline.map((hourData) => {
|
||||
const { time, isNewDay } = hourData;
|
||||
|
||||
return (
|
||||
<Box
|
||||
key={time.format()}
|
||||
return (
|
||||
<Box
|
||||
key={time.format()}
|
||||
style={{
|
||||
width: HOUR_WIDTH,
|
||||
height: '40px',
|
||||
position: 'relative',
|
||||
color: '#a0aec0',
|
||||
borderRight: '1px solid #8DAFAA',
|
||||
cursor: 'pointer',
|
||||
borderLeft: isNewDay ? '2px solid #3BA882' : 'none', // Highlight day boundaries
|
||||
backgroundColor: isNewDay ? '#1E2A27' : '#1B2421', // Subtle background for new days
|
||||
}}
|
||||
onClick={(e) => handleTimeClick(time, e)}
|
||||
>
|
||||
{/* Remove the special day label for new days since we'll show day for all hours */}
|
||||
|
||||
{/* Position time label at the left border of each hour block */}
|
||||
<Text
|
||||
size="sm"
|
||||
style={{
|
||||
width: HOUR_WIDTH,
|
||||
height: '40px',
|
||||
position: 'relative',
|
||||
color: '#a0aec0',
|
||||
borderRight: '1px solid #8DAFAA',
|
||||
cursor: 'pointer',
|
||||
borderLeft: isNewDay ? '2px solid #3BA882' : 'none', // Highlight day boundaries
|
||||
backgroundColor: isNewDay ? '#1E2A27' : '#1B2421', // Subtle background for new days
|
||||
position: 'absolute',
|
||||
top: '8px', // Consistent positioning for all hours
|
||||
left: '4px',
|
||||
transform: 'none',
|
||||
borderRadius: '2px',
|
||||
lineHeight: 1.2,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
onClick={(e) => handleTimeClick(time, e)}
|
||||
>
|
||||
{/* Remove the special day label for new days since we'll show day for all hours */}
|
||||
|
||||
{/* Position time label at the left border of each hour block */}
|
||||
{/* Show day above time for every hour using the same format */}
|
||||
<Text
|
||||
size="sm"
|
||||
span
|
||||
size="xs"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: '8px', // Consistent positioning for all hours
|
||||
left: '4px',
|
||||
transform: 'none',
|
||||
borderRadius: '2px',
|
||||
lineHeight: 1.2,
|
||||
textAlign: 'left',
|
||||
display: 'block',
|
||||
opacity: 0.7,
|
||||
fontWeight: isNewDay ? 600 : 400, // Still emphasize day transitions
|
||||
color: isNewDay ? '#3BA882' : undefined,
|
||||
}}
|
||||
>
|
||||
{/* Show day above time for every hour using the same format */}
|
||||
<Text
|
||||
span
|
||||
size="xs"
|
||||
style={{
|
||||
display: 'block',
|
||||
opacity: 0.7,
|
||||
fontWeight: isNewDay ? 600 : 400, // Still emphasize day transitions
|
||||
color: isNewDay ? '#3BA882' : undefined,
|
||||
}}
|
||||
>
|
||||
{formatDayLabel(time)}{' '}
|
||||
{/* Use same formatDayLabel function for all hours */}
|
||||
</Text>
|
||||
{time.format('h:mm')}
|
||||
<Text span size="xs" ml={1} opacity={0.7}>
|
||||
{time.format('A')}
|
||||
</Text>
|
||||
{formatDayLabel(time)}{' '}
|
||||
{/* Use same formatDayLabel function for all hours */}
|
||||
</Text>
|
||||
{time.format('h:mm')}
|
||||
<Text span size="xs" ml={1} opacity={0.7}>
|
||||
{time.format('A')}
|
||||
</Text>
|
||||
</Text>
|
||||
|
||||
{/* Hour boundary marker - more visible */}
|
||||
<Box
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '1px',
|
||||
backgroundColor: '#27272A',
|
||||
zIndex: 10,
|
||||
}}
|
||||
/>
|
||||
{/* Hour boundary marker - more visible */}
|
||||
<Box
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '1px',
|
||||
backgroundColor: '#27272A',
|
||||
zIndex: 10,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Quarter hour tick marks */}
|
||||
<Box
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0 1px',
|
||||
}}
|
||||
>
|
||||
{[15, 30, 45].map((minute) => (
|
||||
<Box
|
||||
key={minute}
|
||||
style={{
|
||||
width: '1px',
|
||||
height: '8px',
|
||||
backgroundColor: '#718096',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: `${(minute / 60) * 100}%`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
{/* Quarter hour tick marks */}
|
||||
<Box
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
padding: '0 1px',
|
||||
}}
|
||||
>
|
||||
{[15, 30, 45].map((minute) => (
|
||||
<Box
|
||||
key={minute}
|
||||
style={{
|
||||
width: '1px',
|
||||
height: '8px',
|
||||
backgroundColor: '#718096',
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
left: `${(minute / 60) * 100}%`,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue