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

View file

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

View file

@ -41,22 +41,28 @@
--color-work-hours: rgba(255, 255, 255, 0.9);
--color-current-time: #ff0000;
/* Event colors - Updated with month-view-expanded.html color scheme */
--color-event-meeting: #e8f5e8;
--color-event-meeting-border: #4caf50;
--color-event-meeting-hl: #c8e6c9;
--color-event-meal: #fff8e1;
--color-event-meal-border: #ff9800;
--color-event-meal-hl: #ffe0b2;
--color-event-work: #fff8e1;
--color-event-work-border: #ff9800;
--color-event-work-hl: #ffe0b2;
--color-event-milestone: #ffebee;
--color-event-milestone-border: #f44336;
--color-event-milestone-hl: #ffcdd2;
--color-event-personal: #f3e5f5;
--color-event-personal-border: #9c27b0;
--color-event-personal-hl: #e1bee7;
/* Named color palette for events */
--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;
/* Base mix for color-mix() function */
--b-mix: #fff;
/* UI colors */
--color-background: #ffffff;

View file

@ -2,6 +2,8 @@
/* Event base styles */
swp-day-columns swp-event {
--b-text: var(--color-text);
position: absolute;
border-radius: 3px;
overflow: hidden;
@ -10,10 +12,14 @@ swp-day-columns swp-event {
z-index: 10;
left: 2px;
right: 2px;
color: var(--color-text);
font-size: 12px;
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 */
container-type: size;
container-name: event;
@ -25,43 +31,6 @@ swp-day-columns swp-event {
gap: 2px 4px;
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 {
position: absolute;
@ -72,31 +41,10 @@ swp-day-columns swp-event {
width: auto;
}
/* Hover state - highlight colors */
&:hover[data-type="meeting"] {
background: var(--color-event-meeting-hl);
/* Hover state */
&:hover {
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 {
@ -218,10 +166,14 @@ swp-multi-day-event {
white-space: nowrap;
text-overflow: ellipsis;
/* Event type colors */
&[data-type="milestone"] {
background: var(--color-event-milestone);
color: var(--color-event-milestone-border);
/* Color system using color-mix() */
--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);
&:hover {
background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
}
/* Continuation indicators */
@ -259,6 +211,19 @@ swp-multi-day-event {
transform: translateY(-1px);
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 */
@ -351,3 +316,23 @@ swp-event-group swp-event {
swp-allday-container swp-event.transitioning {
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;
border-radius: 3px;
/* Event type colors - normal state */
&[data-type="meeting"] {
background: var(--color-event-meeting);
color: var(--color-text);
}
&[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);
}
/* Color system using color-mix() */
--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);
/* Dragging state */
&.dragging {
opacity: 1;
}
/* Highlight state for all event types */
/* Highlight state */
&.highlight {
&[data-type="meeting"] {
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;
}
background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix)) !important;
}
/* Overflow indicator styling */

File diff suppressed because it is too large Load diff