Calendar/wwwroot/css/calendar-layout-css.css
Janus Knudsen 58d6ad2ed2 Improves header animation performance
Simplifies header animation logic by leveraging CSS Grid for height transitions.

This change removes the direct height animation of the calendar header and relies on CSS Grid's auto row feature to manage the header expansion.
It also removes the manual spacer height calculations in Typescript, and relies on CSS variables to control this.
This results in a smoother and more efficient animation, especially when all-day events are present.
2025-09-01 20:13:15 +02:00

514 lines
No EOL
12 KiB
CSS

/* styles/layout.css - POC Structure Implementation */
/* Calendar wrapper container - full viewport */
.calendar-wrapper {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
box-sizing: border-box;
overflow: hidden;
}
/* Main calendar container - full height */
swp-calendar {
display: grid;
grid-template-rows: auto 1fr;
height: 100vh;
width: 100%;
background: var(--color-background);
position: relative;
overflow: hidden;
}
/* Navigation bar layout */
swp-calendar-nav {
display: grid;
grid-template-columns: auto 1fr auto auto;
align-items: center;
gap: 20px;
padding: 12px 16px;
background: var(--color-background);
border-bottom: 1px solid var(--color-border);
box-shadow: var(--shadow-sm);
}
/* Calendar container grid - POC structure */
swp-calendar-container {
display: grid;
grid-template-columns: 60px 1fr;
grid-template-rows: auto 1fr;
height: 100%;
overflow: hidden;
position: relative;
}
/* Header spacer for time axis alignment */
swp-header-spacer {
grid-column: 1;
grid-row: 1;
height: calc(var(--header-height) + var(--all-day-row-height)); /* Dynamic height including all-day events */
background: var(--color-surface);
border-right: 1px solid var(--color-border);
border-bottom: 1px solid var(--color-border);
z-index: 5; /* Higher than time-axis to cover it when scrolling */
position: relative;
transition: height 150ms ease; /* Smooth height transitions */
}
/* Week container for sliding */
swp-grid-container {
grid-column: 2;
grid-row: 1 / 3;
display: grid;
grid-template-rows: auto 1fr;
position: relative;
width: 100%;
transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1);
overflow: hidden;
}
/* Time axis */
swp-time-axis {
grid-column: 1;
grid-row: 2;
background: var(--color-surface);
border-right: 1px solid var(--color-border);
position: relative;
left: 0;
z-index: 3; /* Lower than header elements so it scrolls behind them */
width: 60px;
overflow: hidden;
height: 100%;
}
/* Time axis content that scrolls */
swp-time-axis-content {
display: flex;
flex-direction: column;
position: relative;
}
swp-hour-marker {
height: var(--hour-height);
padding: 0 8px 8px 8px;
font-size: 0.75rem;
color: var(--color-text-secondary);
display: flex;
align-items: flex-start;
position: relative;
}
swp-hour-marker::before {
content: '';
position: absolute;
top: 0px;
left: 50px;
width: calc(100vw - 60px); /* Full viewport width minus time-axis width */
height: 1px;
background: var(--color-hour-line);
pointer-events: none;
z-index: 2; /* Ensure it appears above other elements */
}
/* Add hour lines to time-grid as background */
swp-time-grid::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
min-width: calc(var(--grid-columns, 7) * var(--day-column-min-width)); /* Dynamic width like swp-grid-lines */
background-image: repeating-linear-gradient(
to bottom,
transparent,
transparent calc(var(--hour-height) - 1px),
var(--color-hour-line) calc(var(--hour-height) - 1px),
var(--color-hour-line) var(--hour-height)
);
pointer-events: none;
z-index: 1;
}
/* Week header - dynamic height based on content */
swp-calendar-header {
display: grid;
grid-template-columns: repeat(var(--grid-columns, 7), minmax(var(--day-column-min-width), 1fr));
grid-template-rows: var(--header-height) auto; /* Row 1: header height, Row 2: auto for all-day events */
min-width: calc(var(--grid-columns, 7) * var(--day-column-min-width)); /* Dynamic width */
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
position: sticky;
top: 0;
z-index: 3; /* Lower than header-spacer so it slides under during horizontal scroll */
height: calc(var(--header-height) + var(--all-day-row-height)); /* Same calculation as spacers */
/* Force scrollbar to appear for alignment */
overflow-y: scroll;
overflow-x: hidden;
/* Firefox - hide scrollbar but keep space */
scrollbar-width: auto; /* Normal width to match content scrollbar */
scrollbar-color: transparent transparent;
}
/* WebKit browsers (Chrome, Safari, Edge) - hide scrollbar but keep space */
swp-calendar-header::-webkit-scrollbar {
width: 17px; /* Match system default scrollbar width */
background: transparent;
}
swp-calendar-header::-webkit-scrollbar-thumb {
background: transparent;
}
swp-calendar-header::-webkit-scrollbar-track {
background: transparent;
}
swp-day-header {
grid-row: 1; /* Explicitly place day headers in row 1 */
pointer-events: auto; /* Ensure header clicks work despite parent scrollbar */
text-align: center;
border-right: 1px solid var(--color-grid-line);
border-bottom: 1px solid var(--color-grid-line);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-top: 7px;
}
swp-day-header:last-child {
border-right: none;
}
/* Resource header styling */
swp-resource-header {
padding: 12px;
text-align: center;
border-right: 1px solid var(--color-grid-line);
border-bottom: 1px solid var(--color-grid-line);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: var(--color-surface);
}
swp-resource-header:last-child {
border-right: none;
}
swp-resource-avatar {
display: block;
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
margin-bottom: 8px;
background: var(--color-border);
}
swp-resource-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
swp-resource-name {
display: block;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text);
text-align: center;
}
swp-day-name {
display: block;
font-weight: 500;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-day-date {
display: block;
font-size: 20px;
font-weight: 600;
margin-top: 4px;
height: 41px;
}
swp-day-header[data-today="true"] swp-day-date {
color: var(--color-primary);
background: rgba(33, 150, 243, 0.1);
border-radius: 50%;
width: 41px;
height: 41px;
display: flex;
align-items: center;
justify-content: center;
margin: 4px auto 0;
}
/* All-day container - initially hidden, animated in when first event is dragged */
swp-allday-container {
grid-column: 1 / -1; /* Span all columns */
grid-row: 2; /* Second row of calendar header */
display: grid;
grid-template-columns: repeat(var(--grid-columns, 7), minmax(var(--day-column-min-width), 1fr));
grid-template-rows: repeat(1, auto); /* Default to 1 row, dynamically updated by JS */
gap: 2px;
padding: 2px;
align-items: center;
overflow: hidden;
}
/* All-day events in containers */
swp-allday-event {
height: 22px; /* Fixed height for consistent stacking */
background: #ff9800; /* Default orange background */
display: flex;
align-items: center;
justify-content: flex-start;
color: #fff;
font-size: 0.75rem;
padding: 2px 4px;
border-radius: 3px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
border-left: 3px solid rgba(0, 0, 0, 0.2);
}
swp-allday-event:last-child {
margin-bottom: 0;
}
/* Scrollable content */
swp-scrollable-content {
overflow-y: auto;
overflow-x: auto;
scroll-behavior: smooth;
position: relative;
display: grid;
/* Height and width will be set dynamically by ScrollManager via ResizeObserver */
}
/* Style native scrollbars for Webkit browsers (Chrome, Safari, Edge) */
swp-scrollable-content::-webkit-scrollbar {
width: var(--scrollbar-width, 12px);
height: var(--scrollbar-width, 12px);
}
swp-scrollable-content::-webkit-scrollbar-track {
background: var(--scrollbar-track-color, #f0f0f0);
}
swp-scrollable-content::-webkit-scrollbar-thumb {
background: var(--scrollbar-color, #666);
border-radius: var(--scrollbar-border-radius, 6px);
}
swp-scrollable-content::-webkit-scrollbar-thumb:hover {
background: var(--scrollbar-hover-color, #333);
}
/* Style native scrollbars for Firefox */
swp-scrollable-content {
scrollbar-width: auto; /* Let it use the webkit width */
scrollbar-color: var(--scrollbar-color, #666) var(--scrollbar-track-color, #f0f0f0);
}
/* Fit to width mode - disable horizontal scroll */
swp-calendar[data-fit-to-width="true"] swp-scrollable-content {
overflow-x: hidden;
}
/* Time grid */
swp-time-grid {
position: relative;
height: calc((var(--day-end-hour) - var(--day-start-hour)) * var(--hour-height));
}
/* Global work hours overlay - now disabled, replaced by per-column overlays */
swp-time-grid::before {
content: '';
position: absolute;
top: 0;
height: 0;
left: 0;
right: 0;
background: transparent;
min-width: calc(var(--grid-columns, 7) * var(--day-column-min-width));
pointer-events: none;
display: none; /* Disabled - using per-column overlays instead */
}
/* Grid lines */
swp-grid-lines {
position: absolute;
top: 0px;
left: 0;
right: 0;
bottom: 0;
min-width: calc(var(--grid-columns, 7) * var(--day-column-min-width)); /* Dynamic width */
pointer-events: none;
z-index: var(--z-grid);
background-image: repeating-linear-gradient(
to bottom,
transparent,
transparent calc(var(--hour-height) / 4 - 1px),
var(--color-grid-line-light) calc(var(--hour-height) / 4 - 1px),
var(--color-grid-line-light) calc(var(--hour-height) / 4)
);
}
/* Day columns */
swp-day-columns {
position: absolute;
inset: 0;
display: grid;
grid-template-columns: repeat(var(--grid-columns, 7), minmax(var(--day-column-min-width), 1fr));
min-width: calc(var(--grid-columns, 7) * var(--day-column-min-width)); /* Dynamic width */
}
swp-day-column {
position: relative;
border-right: 1px solid var(--color-grid-line);
min-width: var(--day-column-min-width);
background: var(--color-event-grid);
}
/* Per-column non-work hours overlays */
/* Before work overlay */
swp-day-column::before {
content: '';
position: absolute;
left: 0;
right: 0;
background: var(--color-non-work-hours);
pointer-events: none;
z-index: 2;
/* Before work period - from day start to work start */
top: 0;
height: var(--before-work-height, 0px);
opacity: 0.3;
}
/* After work overlay */
swp-day-column::after {
content: '';
position: absolute;
left: 0;
right: 0;
background: var(--color-non-work-hours);
pointer-events: none;
z-index: 2;
/* After work period - from work end to day end */
top: var(--after-work-top, 100%);
bottom: 0;
opacity: 0.3;
}
/* Full day overlay when day is off */
swp-day-column[data-work-hours="off"] {
background: var(--color-non-work-hours);
}
swp-day-column[data-work-hours="off"]::before,
swp-day-column[data-work-hours="off"]::after {
display: none;
}
swp-day-column:last-child {
border-right: none;
}
/* Resource column styling */
swp-resource-column {
position: relative;
border-right: 1px solid var(--color-grid-line);
min-width: var(--day-column-min-width);
background: var(--color-event-grid);
}
swp-resource-column:last-child {
border-right: none;
}
swp-events-layer {
position: absolute;
inset: 0;
display: block;
z-index: var(--z-event);
pointer-events: none; /* Allow clicks to pass through to day column */
}
swp-event {
pointer-events: auto;
}
/* Current time indicator */
swp-current-time-indicator {
position: absolute;
left: 0;
right: 0;
height: 2px;
background: var(--color-current-time);
pointer-events: none;
z-index: var(--z-current-time);
/* Time label */
&::before {
content: attr(data-time);
position: absolute;
left: -55px;
top: -10px;
background: var(--color-current-time);
color: white;
padding: 2px 6px;
font-size: 0.75rem;
border-radius: 3px;
white-space: nowrap;
}
/* Animated dot */
&::after {
content: '';
position: absolute;
right: -4px;
top: -4px;
width: 10px;
height: 10px;
background: var(--color-current-time);
border-radius: 50%;
box-shadow: 0 0 0 2px rgba(255, 0, 0, 0.3);
}
}
/* Week navigation animations - simplified */
swp-calendar-container.week-transition {
transition: opacity 300ms ease;
}
swp-calendar-container.week-transition-out {
opacity: 0.5;
}