Enhances event color styling with flexible metadata system

Introduces a new color customization approach for calendar events using CSS custom properties and metadata

- Adds support for dynamic color assignment via event metadata
- Implements a flexible color utility class system
- Replaces hardcoded event type colors with a more generic color-mix() approach
- Provides broader color palette for event styling
This commit is contained in:
Janus C. H. Knudsen 2025-11-25 23:48:30 +01:00
parent d8b9f6dabd
commit be551f88e5
7 changed files with 269 additions and 4135 deletions

147
.workbench/event-colors.txt Normal file
View file

@ -0,0 +1,147 @@
<!doctype html>
<html lang="da">
<head>
<meta charset="utf-8" />
<title>Event Farvesystem Demo</title>
<style>
:root {
/* Palette */
--b-color-red: #e53935;
--b-color-pink: #d81b60;
--b-color-magenta: #c200c2;
--b-color-purple: #8e24aa;
--b-color-violet: #5e35b1;
--b-color-deep-purple: #4527a0;
--b-color-indigo: #3949ab;
--b-color-blue: #1e88e5;
--b-color-light-blue: #03a9f4;
--b-color-cyan: #3bc9db;
--b-color-teal: #00897b;
--b-color-green: #43a047;
--b-color-light-green: #8bc34a;
--b-color-lime: #c0ca33;
--b-color-yellow: #fdd835;
--b-color-amber: #ffb300;
--b-color-orange: #fb8c00;
--b-color-deep-orange: #f4511e;
/* Basismix (lysning). Kan skiftes til #000 for mørkning */
--b-mix: #fff;
}
body {
font-family: system-ui, sans-serif;
background: #f3f3f3;
margin: 0;
padding: 2rem;
}
h1 { margin-top: 0; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1rem;
}
/* -----------------------------------------------------------
EVENT KOMPONENT
----------------------------------------------------------- */
.event {
--b-text: var(--b-primary);
display: flex;
border-radius: .5rem;
overflow: hidden;
cursor: pointer;
padding: 1rem 1rem 1rem .75rem;
background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-text);
border-left: 6px solid var(--b-primary);
transition:
background-color .2s ease,
border-color .2s ease;
}
.event:hover {
background-color: color-mix(in srgb, var(--b-primary) 25%, var(--b-mix));
}
.event-title {
font-weight: 600;
margin-bottom: .25rem;
}
.event-meta {
font-size: .85rem;
opacity: .8;
}
/* -----------------------------------------------------------
eksempel på UTILITY-KLASSER
----------------------------------------------------------- */
.is-red { --b-primary: var(--b-color-red); }
.is-blue { --b-primary: var(--b-color-blue); }
.is-green { --b-primary: var(--b-color-green); }
.is-magenta { --b-primary: var(--b-color-magenta); }
.is-amber { --b-primary: var(--b-color-amber); }
.is-orange { --b-primary: var(--b-color-orange); }
</style>
</head>
<body>
<h1>Event Farvesystem Demo</h1>
<p>Baggrunden er dæmpet primærfarve, hover gør den mørkere, venstre kant og tekst bruger den rene farve.</p>
<div class="grid">
<div class="event is-blue">
<div>
<div class="event-title">Blå event</div>
<div class="event-meta">.is-blue</div>
</div>
</div>
<div class="event is-red">
<div>
<div class="event-title">Rød event</div>
<div class="event-meta">.is-red</div>
</div>
</div>
<div class="event is-green">
<div>
<div class="event-title">Grøn event</div>
<div class="event-meta">.is-green</div>
</div>
</div>
<div class="event is-magenta">
<div>
<div class="event-title">Magenta event</div>
<div class="event-meta">.is-magenta</div>
</div>
</div>
<div class="event is-amber">
<div>
<div class="event-title">Amber event</div>
<div class="event-meta">.is-amber</div>
</div>
</div>
<div class="event is-orange">
<div>
<div class="event-title">Orange event</div>
<div class="event-meta">.is-orange</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -296,6 +296,11 @@ export class SwpEventElement extends BaseSwpEventElement {
element.dataset.type = event.type; element.dataset.type = event.type;
element.dataset.duration = event.metadata?.duration?.toString() || '60'; element.dataset.duration = event.metadata?.duration?.toString() || '60';
// Apply color class from metadata
if (event.metadata?.color) {
element.classList.add(`is-${event.metadata.color}`);
}
return element; return element;
} }
@ -373,6 +378,11 @@ export class SwpAllDayEventElement extends BaseSwpEventElement {
element.dataset.allday = 'true'; element.dataset.allday = 'true';
element.textContent = event.title; element.textContent = event.title;
// Apply color class from metadata
if (event.metadata?.color) {
element.classList.add(`is-${event.metadata.color}`);
}
return element; return element;
} }
} }

View file

@ -141,7 +141,7 @@ async function initializeCalendar(): Promise<void> {
builder.registerType(MockAuditRepository).as<IApiRepository<IAuditEntry>>(); builder.registerType(MockAuditRepository).as<IApiRepository<IAuditEntry>>();
let calendarMode = 'date' ; let calendarMode = 'resource' ;
// Register DataSource and HeaderRenderer based on mode // Register DataSource and HeaderRenderer based on mode
if (calendarMode === 'resource') { if (calendarMode === 'resource') {
builder.registerType(ResourceColumnDataSource).as<IColumnDataSource>(); builder.registerType(ResourceColumnDataSource).as<IColumnDataSource>();

View file

@ -41,22 +41,28 @@
--color-work-hours: rgba(255, 255, 255, 0.9); --color-work-hours: rgba(255, 255, 255, 0.9);
--color-current-time: #ff0000; --color-current-time: #ff0000;
/* Event colors - Updated with month-view-expanded.html color scheme */ /* Named color palette for events */
--color-event-meeting: #e8f5e8; --b-color-red: #e53935;
--color-event-meeting-border: #4caf50; --b-color-pink: #d81b60;
--color-event-meeting-hl: #c8e6c9; --b-color-magenta: #c200c2;
--color-event-meal: #fff8e1; --b-color-purple: #8e24aa;
--color-event-meal-border: #ff9800; --b-color-violet: #5e35b1;
--color-event-meal-hl: #ffe0b2; --b-color-deep-purple: #4527a0;
--color-event-work: #fff8e1; --b-color-indigo: #3949ab;
--color-event-work-border: #ff9800; --b-color-blue: #1e88e5;
--color-event-work-hl: #ffe0b2; --b-color-light-blue: #03a9f4;
--color-event-milestone: #ffebee; --b-color-cyan: #3bc9db;
--color-event-milestone-border: #f44336; --b-color-teal: #00897b;
--color-event-milestone-hl: #ffcdd2; --b-color-green: #43a047;
--color-event-personal: #f3e5f5; --b-color-light-green: #8bc34a;
--color-event-personal-border: #9c27b0; --b-color-lime: #c0ca33;
--color-event-personal-hl: #e1bee7; --b-color-yellow: #fdd835;
--b-color-amber: #ffb300;
--b-color-orange: #fb8c00;
--b-color-deep-orange: #f4511e;
/* Base mix for color-mix() function */
--b-mix: #fff;
/* UI colors */ /* UI colors */
--color-background: #ffffff; --color-background: #ffffff;

View file

@ -2,6 +2,8 @@
/* Event base styles */ /* Event base styles */
swp-day-columns swp-event { swp-day-columns swp-event {
--b-text: var(--color-text);
position: absolute; position: absolute;
border-radius: 3px; border-radius: 3px;
overflow: hidden; overflow: hidden;
@ -10,10 +12,14 @@ swp-day-columns swp-event {
z-index: 10; z-index: 10;
left: 2px; left: 2px;
right: 2px; right: 2px;
color: var(--color-text);
font-size: 12px; font-size: 12px;
padding: 4px 6px; padding: 4px 6px;
/* Color system using color-mix() */
background-color: color-mix(in srgb, var(--b-primary) 10%, var(--b-mix));
color: var(--b-text);
border-left: 4px solid var(--b-primary);
/* Enable container queries for responsive layout */ /* Enable container queries for responsive layout */
container-type: size; container-type: size;
container-name: event; container-name: event;
@ -25,43 +31,6 @@ swp-day-columns swp-event {
gap: 2px 4px; gap: 2px 4px;
align-items: start; align-items: start;
/* Event types */
&[data-type="meeting"] {
background: var(--color-event-meeting);
border-left: 4px solid var(--color-event-meeting-border);
color: var(--color-text);
}
&[data-type="meal"] {
background: var(--color-event-meal);
border-left: 4px solid var(--color-event-meal-border);
color: var(--color-text);
}
&[data-type="work"] {
background: var(--color-event-work);
border-left: 4px solid var(--color-event-work-border);
color: var(--color-text);
}
&[data-type="milestone"] {
background: var(--color-event-milestone);
border-left: 4px solid var(--color-event-milestone-border);
color: var(--color-text);
}
&[data-type="personal"] {
background: var(--color-event-personal);
border-left: 4px solid var(--color-event-personal-border);
color: var(--color-text);
}
&[data-type="deadline"] {
background: var(--color-event-milestone);
border-left: 4px solid var(--color-event-milestone-border);
color: var(--color-text);
}
/* Dragging state */ /* Dragging state */
&.dragging { &.dragging {
position: absolute; position: absolute;
@ -72,31 +41,10 @@ swp-day-columns swp-event {
width: auto; width: auto;
} }
/* Hover state - highlight colors */ /* Hover state */
&:hover[data-type="meeting"] { &:hover {
background: var(--color-event-meeting-hl); background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
} }
&:hover[data-type="meal"] {
background: var(--color-event-meal-hl);
}
&:hover[data-type="work"] {
background: var(--color-event-work-hl);
}
&:hover[data-type="milestone"] {
background: var(--color-event-milestone-hl);
}
&:hover[data-type="personal"] {
background: var(--color-event-personal-hl);
}
&:hover[data-type="deadline"] {
background: var(--color-event-milestone-hl);
}
} }
swp-day-columns swp-event:hover { swp-day-columns swp-event:hover {
@ -218,10 +166,14 @@ swp-multi-day-event {
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
/* Event type colors */ /* Color system using color-mix() */
&[data-type="milestone"] { --b-text: var(--color-text);
background: var(--color-event-milestone); background-color: color-mix(in srgb, var(--b-primary) 10%, var(--b-mix));
color: var(--color-event-milestone-border); color: var(--b-text);
border-left: 4px solid var(--b-primary);
&:hover {
background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
} }
/* Continuation indicators */ /* Continuation indicators */
@ -259,6 +211,19 @@ swp-multi-day-event {
transform: translateY(-1px); transform: translateY(-1px);
box-shadow: var(--shadow-sm); box-shadow: var(--shadow-sm);
} }
/* All-day events */
swp-allday-event {
--b-text: var(--color-text);
background-color: color-mix(in srgb, var(--b-primary) 10%, var(--b-mix));
color: var(--b-text);
border-left: 4px solid var(--b-primary);
cursor: pointer;
transition: background-color 200ms ease;
&:hover {
background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
}
}
} }
/* Event creation preview */ /* Event creation preview */
@ -351,3 +316,23 @@ swp-event-group swp-event {
swp-allday-container swp-event.transitioning { swp-allday-container swp-event.transitioning {
transition: grid-area 200ms ease-out, grid-row 200ms ease-out, grid-column 200ms ease-out; transition: grid-area 200ms ease-out, grid-row 200ms ease-out, grid-column 200ms ease-out;
} }
/* Color utility classes */
.is-red { --b-primary: var(--b-color-red); }
.is-pink { --b-primary: var(--b-color-pink); }
.is-magenta { --b-primary: var(--b-color-magenta); }
.is-purple { --b-primary: var(--b-color-purple); }
.is-violet { --b-primary: var(--b-color-violet); }
.is-deep-purple { --b-primary: var(--b-color-deep-purple); }
.is-indigo { --b-primary: var(--b-color-indigo); }
.is-blue { --b-primary: var(--b-color-blue); }
.is-light-blue { --b-primary: var(--b-color-light-blue); }
.is-cyan { --b-primary: var(--b-color-cyan); }
.is-teal { --b-primary: var(--b-color-teal); }
.is-green { --b-primary: var(--b-color-green); }
.is-light-green { --b-primary: var(--b-color-light-green); }
.is-lime { --b-primary: var(--b-color-lime); }
.is-yellow { --b-primary: var(--b-color-yellow); }
.is-amber { --b-primary: var(--b-color-amber); }
.is-orange { --b-primary: var(--b-color-orange); }
.is-deep-orange { --b-primary: var(--b-color-deep-orange); }

View file

@ -322,67 +322,20 @@ swp-allday-container {
font-size: 0.75rem; font-size: 0.75rem;
border-radius: 3px; border-radius: 3px;
/* Event type colors - normal state */ /* Color system using color-mix() */
&[data-type="meeting"] { --b-text: var(--color-text);
background: var(--color-event-meeting); background-color: color-mix(in srgb, var(--b-primary) 10%, var(--b-mix));
color: var(--color-text); color: var(--b-text);
} border-left: 4px solid var(--b-primary);
&[data-type="meal"] {
background: var(--color-event-meal);
color: var(--color-text);
}
&[data-type="work"] {
background: var(--color-event-work);
color: var(--color-text);
}
&[data-type="milestone"] {
background: var(--color-event-milestone);
color: var(--color-text);
}
&[data-type="personal"] {
background: var(--color-event-personal);
color: var(--color-text);
}
&[data-type="deadline"] {
background: var(--color-event-milestone);
color: var(--color-text);
}
/* Dragging state */ /* Dragging state */
&.dragging { &.dragging {
opacity: 1; opacity: 1;
} }
/* Highlight state for all event types */ /* Highlight state */
&.highlight { &.highlight {
&[data-type="meeting"] { background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix)) !important;
background: var(--color-event-meeting-hl) !important;
}
&[data-type="meal"] {
background: var(--color-event-meal-hl) !important;
}
&[data-type="work"] {
background: var(--color-event-work-hl) !important;
}
&[data-type="milestone"] {
background: var(--color-event-milestone-hl) !important;
}
&[data-type="personal"] {
background: var(--color-event-personal-hl) !important;
}
&[data-type="deadline"] {
background: var(--color-event-milestone-hl) !important;
}
} }
/* Overflow indicator styling */ /* Overflow indicator styling */

File diff suppressed because it is too large Load diff