Refactors drag and drop logic to dynamically find the dragged element, ensuring correct behavior even when the DOM changes during the drag operation. Creates all-day container if it doesn't exist. This resolves issues where drag and drop operations would fail if the original element was no longer present in the DOM or if the container didn't exist.
526 lines
No EOL
12 KiB
CSS
526 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;
|
|
}
|
|
|
|
|
|
|
|
/* 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: -1px;
|
|
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 */
|
|
|
|
/* All-day events container */
|
|
swp-allday-container {
|
|
grid-column: 1 / -1;
|
|
grid-row: 2;
|
|
display: grid;
|
|
grid-template-columns: repeat(var(--grid-columns, 7), minmax(var(--day-column-min-width), 1fr));
|
|
grid-template-rows: repeat(1, auto);
|
|
gap: 2px;
|
|
padding: 2px;
|
|
align-items: center;
|
|
overflow: hidden;
|
|
}
|
|
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-day-columns 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;
|
|
} |